Skip to content

Commit d54bc3a

Browse files
authored
Add github actions for generator and publish (#97)
1 parent 7c32e29 commit d54bc3a

File tree

8 files changed

+304
-144
lines changed

8 files changed

+304
-144
lines changed

.gitignore

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -180,9 +180,6 @@ DocProject/Help/*.hhp
180180
DocProject/Help/Html2
181181
DocProject/Help/html
182182

183-
# Click-Once directory
184-
publish/
185-
186183
# Publish Web Output
187184
*.[Pp]ublish.xml
188185
*.azurePubxml

action.yml

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,25 @@ branding:
77

88
inputs:
99
prefix:
10-
description: 'The relative location of the documentation'
10+
description: 'Path prefix for all urls'
1111
required: false
1212

1313
runs:
14-
using: 'docker'
15-
image: "docker://ghcr.io/elastic/docs-builder:edge"
16-
14+
- id: repo-basename
15+
run: 'echo "value=`basename ${{ github.repository }}`" >> $GITHUB_OUTPUT'
16+
- uses: actions/checkout@v4
17+
- name: Setup Pages
18+
id: pages
19+
uses: actions/[email protected]
20+
- name: Build documentation
21+
uses: elastic/docs-builder@main
22+
with:
23+
prefix: '${{ steps.repo-basename.outputs.value }}'
24+
- name: Upload artifact
25+
uses: actions/[email protected]
26+
with:
27+
path: .artifacts/docs/html
28+
29+
- name: Deploy artifact
30+
id: deployment
31+
uses: actions/[email protected]

actions/generator/action.yml

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
name: 'Documentation Generator'
2+
description: 'Generates a random yet deterministic documentation set'
3+
4+
branding:
5+
icon: 'filter'
6+
color: 'red'
7+
8+
inputs:
9+
output:
10+
description: 'Path to output the documentation'
11+
required: false
12+
13+
runs:
14+
using: 'docker'
15+
image: "docker://ghcr.io/elastic/docs-generator:edge"
16+

actions/publish/action.yml

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
name: 'Documentation Publisher'
2+
description: 'Builds and publishes documentation to github pages'
3+
4+
branding:
5+
icon: 'filter'
6+
color: 'red'
7+
8+
outputs:
9+
page_url:
10+
description: "The github actions url"
11+
value: ${{steps.deployment.outputs.page_url}}
12+
13+
runs:
14+
using: "composite"
15+
steps:
16+
- id: repo-basename
17+
run: 'echo "value=`basename ${{ github.repository }}`" >> $GITHUB_OUTPUT'
18+
- uses: actions/checkout@v4
19+
- name: Setup Pages
20+
id: pages
21+
uses: actions/[email protected]
22+
- name: Build documentation
23+
uses: elastic/docs-builder@main
24+
with:
25+
prefix: '${{ steps.repo-basename.outputs.value }}'
26+
- name: Upload artifact
27+
uses: actions/[email protected]
28+
with:
29+
path: .artifacts/docs/html
30+
31+
- name: Deploy artifact
32+
id: deployment
33+
uses: actions/[email protected]
34+
35+

docs-builder.sln

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,18 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elastic.Markdown.Tests", "t
3232
EndProject
3333
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "docs-generator", "src\docs-generator\docs-generator.csproj", "{61904527-9753-4379-B546-56B6A29073AC}"
3434
EndProject
35+
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "actions", "actions", "{245023D2-D3CA-47B9-831D-DAB91A2FFDC7}"
36+
EndProject
37+
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "generator", "generator", "{1C340CCF-9AAC-4163-A7BB-60528076E98B}"
38+
ProjectSection(SolutionItems) = preProject
39+
actions\generator\action.yml = actions\generator\action.yml
40+
EndProjectSection
41+
EndProject
42+
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "publish", "publish", "{CD2887E3-BDA9-434B-A5BF-9ED38DE20332}"
43+
ProjectSection(SolutionItems) = preProject
44+
actions\publish\action.yml = actions\publish\action.yml
45+
EndProjectSection
46+
EndProject
3547
Global
3648
GlobalSection(SolutionConfigurationPlatforms) = preSolution
3749
Debug|Any CPU = Debug|Any CPU
@@ -72,5 +84,7 @@ Global
7284
{01F05AD0-E0E0-401F-A7EC-905928E1E9F0} = {BE6011CC-1200-4957-B01F-FCCA10C5CF5A}
7385
{B27C5107-128B-465A-B8F8-8985399E4CFB} = {67B576EE-02FA-4F9B-94BC-3630BC09ECE5}
7486
{61904527-9753-4379-B546-56B6A29073AC} = {BE6011CC-1200-4957-B01F-FCCA10C5CF5A}
87+
{1C340CCF-9AAC-4163-A7BB-60528076E98B} = {245023D2-D3CA-47B9-831D-DAB91A2FFDC7}
88+
{CD2887E3-BDA9-434B-A5BF-9ED38DE20332} = {245023D2-D3CA-47B9-831D-DAB91A2FFDC7}
7589
EndGlobalSection
7690
EndGlobal
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
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+
namespace Documentation.Generator.Cli;
5+
6+
/// <summary>
7+
/// This exists temporarily for .NET 8.
8+
/// The container builds prepends `dotnet [app].dll` as arguments
9+
/// Fixed in .NET 9: https://github.com/dotnet/sdk-container-builds/issues/559
10+
/// </summary>
11+
public class Arguments
12+
{
13+
public required string[] Args { get; init; }
14+
public required bool IsHelp { get; init; }
15+
16+
public static Arguments Filter(string[] args) =>
17+
new Arguments { Args = Enumerate(args).ToArray(), IsHelp = args.Contains("-h") || args.Contains("--help") };
18+
19+
private static IEnumerable<string> Enumerate(string[] args)
20+
{
21+
for (var i = 0; i < args.Length; i++)
22+
{
23+
switch (i)
24+
{
25+
case 0 when args[i] == "dotnet":
26+
case 1 when args[i].EndsWith(".dll"):
27+
continue;
28+
default:
29+
yield return args[i];
30+
break;
31+
}
32+
}
33+
}
34+
}

src/docs-generator/Cli/Commands.cs

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

Comments
 (0)