Skip to content

Commit

Permalink
Add github actions for generator and publish (#97)
Browse files Browse the repository at this point in the history
  • Loading branch information
Mpdreamz authored Dec 9, 2024
1 parent 7c32e29 commit d54bc3a
Show file tree
Hide file tree
Showing 8 changed files with 304 additions and 144 deletions.
3 changes: 0 additions & 3 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -180,9 +180,6 @@ DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html

# Click-Once directory
publish/

# Publish Web Output
*.[Pp]ublish.xml
*.azurePubxml
Expand Down
23 changes: 19 additions & 4 deletions action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,25 @@ branding:

inputs:
prefix:
description: 'The relative location of the documentation'
description: 'Path prefix for all urls'
required: false

runs:
using: 'docker'
image: "docker://ghcr.io/elastic/docs-builder:edge"

- id: repo-basename
run: 'echo "value=`basename ${{ github.repository }}`" >> $GITHUB_OUTPUT'
- uses: actions/checkout@v4
- name: Setup Pages
id: pages
uses: actions/[email protected]
- name: Build documentation
uses: elastic/docs-builder@main
with:
prefix: '${{ steps.repo-basename.outputs.value }}'
- name: Upload artifact
uses: actions/[email protected]
with:
path: .artifacts/docs/html

- name: Deploy artifact
id: deployment
uses: actions/[email protected]
16 changes: 16 additions & 0 deletions actions/generator/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
name: 'Documentation Generator'
description: 'Generates a random yet deterministic documentation set'

branding:
icon: 'filter'
color: 'red'

inputs:
output:
description: 'Path to output the documentation'
required: false

runs:
using: 'docker'
image: "docker://ghcr.io/elastic/docs-generator:edge"

35 changes: 35 additions & 0 deletions actions/publish/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
name: 'Documentation Publisher'
description: 'Builds and publishes documentation to github pages'

branding:
icon: 'filter'
color: 'red'

outputs:
page_url:
description: "The github actions url"
value: ${{steps.deployment.outputs.page_url}}

runs:
using: "composite"
steps:
- id: repo-basename
run: 'echo "value=`basename ${{ github.repository }}`" >> $GITHUB_OUTPUT'
- uses: actions/checkout@v4
- name: Setup Pages
id: pages
uses: actions/[email protected]
- name: Build documentation
uses: elastic/docs-builder@main
with:
prefix: '${{ steps.repo-basename.outputs.value }}'
- name: Upload artifact
uses: actions/[email protected]
with:
path: .artifacts/docs/html

- name: Deploy artifact
id: deployment
uses: actions/[email protected]


14 changes: 14 additions & 0 deletions docs-builder.sln
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,18 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elastic.Markdown.Tests", "t
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "docs-generator", "src\docs-generator\docs-generator.csproj", "{61904527-9753-4379-B546-56B6A29073AC}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "actions", "actions", "{245023D2-D3CA-47B9-831D-DAB91A2FFDC7}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "generator", "generator", "{1C340CCF-9AAC-4163-A7BB-60528076E98B}"
ProjectSection(SolutionItems) = preProject
actions\generator\action.yml = actions\generator\action.yml
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "publish", "publish", "{CD2887E3-BDA9-434B-A5BF-9ED38DE20332}"
ProjectSection(SolutionItems) = preProject
actions\publish\action.yml = actions\publish\action.yml
EndProjectSection
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -72,5 +84,7 @@ Global
{01F05AD0-E0E0-401F-A7EC-905928E1E9F0} = {BE6011CC-1200-4957-B01F-FCCA10C5CF5A}
{B27C5107-128B-465A-B8F8-8985399E4CFB} = {67B576EE-02FA-4F9B-94BC-3630BC09ECE5}
{61904527-9753-4379-B546-56B6A29073AC} = {BE6011CC-1200-4957-B01F-FCCA10C5CF5A}
{1C340CCF-9AAC-4163-A7BB-60528076E98B} = {245023D2-D3CA-47B9-831D-DAB91A2FFDC7}
{CD2887E3-BDA9-434B-A5BF-9ED38DE20332} = {245023D2-D3CA-47B9-831D-DAB91A2FFDC7}
EndGlobalSection
EndGlobal
34 changes: 34 additions & 0 deletions src/docs-generator/Cli/ArgsFilter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// 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
namespace Documentation.Generator.Cli;

/// <summary>
/// This exists temporarily for .NET 8.
/// The container builds prepends `dotnet [app].dll` as arguments
/// Fixed in .NET 9: https://github.com/dotnet/sdk-container-builds/issues/559
/// </summary>
public class Arguments
{
public required string[] Args { get; init; }
public required bool IsHelp { get; init; }

public static Arguments Filter(string[] args) =>
new Arguments { Args = Enumerate(args).ToArray(), IsHelp = args.Contains("-h") || args.Contains("--help") };

private static IEnumerable<string> Enumerate(string[] args)
{
for (var i = 0; i < args.Length; i++)
{
switch (i)
{
case 0 when args[i] == "dotnet":
case 1 when args[i].EndsWith(".dll"):
continue;
default:
yield return args[i];
break;
}
}
}
}
160 changes: 160 additions & 0 deletions src/docs-generator/Cli/Commands.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
// 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 Actions.Core.Services;
using ConsoleAppFramework;
using Documentation.Generator.Domain;
using Microsoft.Extensions.Logging;

namespace Documentation.Generator.Cli;

internal class Commands(ILoggerFactory logger, ICoreService githubActionsService)
{
private readonly ILogger _logger = logger.CreateLogger<Commands>();

[Command("")]
public async Task<int> Generate(
int? seedFileSystem = null,
int? seedContent = null,
string? output = null,
bool? clear = null,
Cancel ctx = default
)
{
output ??= githubActionsService.GetInput("output");
var cleanOutputDirectory = clear ?? true;
var outputFolder = !string.IsNullOrWhiteSpace(output)
? new DirectoryInfo(output)
: new DirectoryInfo(Path.Combine(Paths.Root.FullName, ".artifacts/docs/markdown"));
var stateFile = new FileInfo(Path.Combine(outputFolder.FullName, "generator.state"));

LoadStateFromFile(stateFile, clear, ref seedFileSystem, ref cleanOutputDirectory);

Determinism.Random = new Determinism(seedFileSystem, seedContent);

_logger.LogInformation(
$"Running generator with file seed: {Determinism.Random.SeedFileSystem} and content seed: {Determinism.Random.SeedContent}");

Generators.FolderName.UseSeed(Determinism.Random.SeedFileSystem);
Generators.File.UseSeed(Determinism.Random.SeedFileSystem);
Generators.Section.UseSeed(Determinism.Random.SeedContent);

Generators.FolderNames = Generators.FolderName
.Generate(Determinism.Random.FileSystem.Number(3, 15))
.SelectMany(p => Generators.CreateSubPaths(p.Folder, Determinism.Random.FileSystem.Number(0, 3), 0))
.Distinct()
.ToArray();

var folders = new List<Folder>();
foreach (var folder in Generators.FolderNames)
{
var mdFolder = new Folder
{
Path = folder,
Files = Generators.File
.Generate(Determinism.Random.FileSystem.Number(1, 4))
.Select(f =>
{
f.Directory = folder;
return f;
})
.ToArray()
};
folders.Add(mdFolder);
}

var files = folders.SelectMany(f => f.Files).ToArray();
foreach (var folder in folders)
{
foreach (var file in folder.Files)
{
var length = Determinism.Random.Contents.Number(1, 10);
file.Links = Enumerable.Range(0, length)
.Select(i => files[Determinism.Random.Contents.Number(0, files.Length - 1)])
.Select(f => f.GetRandomLink())
.ToList();
file.RewriteLinksIntoSections();
}
}

_logger.LogInformation($"Writing to {outputFolder.FullName}");

if (outputFolder.Exists && cleanOutputDirectory)
Directory.Delete(outputFolder.FullName, true);

var updateFiles = files
.Where(f => cleanOutputDirectory || f.IncludeInUpdate)
.ToArray();
foreach (var file in updateFiles)
{
var directory = Path.Combine(outputFolder.FullName, file.Directory);
_logger.LogInformation($"Writing to {directory}");
Directory.CreateDirectory(directory);

WriteMarkdownFile(outputFolder, file);
}

var name = $"random-docset-{seedContent}-{seedFileSystem}";
WriteIndexMarkdownFile(name, outputFolder);

var docset = Path.Combine(outputFolder.FullName, "docset.yml");
File.WriteAllText(docset, $"project: {name}{Environment.NewLine}");
File.AppendAllText(docset, $"toc:{Environment.NewLine}");
foreach (var folder in folders)
File.AppendAllText(docset, $" - folder: {folder.Path}{Environment.NewLine}");

File.AppendAllText(docset, $" - file: index.md{Environment.NewLine}");
File.AppendAllText(docset, Environment.NewLine);

File.WriteAllText(stateFile.FullName, $"{Determinism.Random.SeedFileSystem}|{Determinism.Random.SeedContent}");

return await Task.FromResult(0);
}

private void WriteIndexMarkdownFile(string name, DirectoryInfo directoryInfo)
{
var filePath = Path.Combine(directoryInfo.FullName, "index.md");
File.WriteAllText(filePath,
$"""
---
title: {name} Documentation Set
---
""");
File.AppendAllText(filePath, "This docset is generated using docs-generator");
File.AppendAllText(filePath, Environment.NewLine);
}

private void WriteMarkdownFile(DirectoryInfo directoryInfo, MarkdownFile markdownFile)
{
var filePath = Path.Combine(directoryInfo.FullName, markdownFile.RelativePath);
File.WriteAllText(filePath,
$"""
---
title: {markdownFile.Title}
---
""");
foreach (var section in markdownFile.Sections)
{
File.AppendAllText(filePath, Environment.NewLine);
var header = new string('#', section.Level);
File.AppendAllText(filePath, $"{header} {section.Header}{Environment.NewLine}");
File.AppendAllText(filePath, Environment.NewLine);

File.AppendAllText(filePath, section.Paragraphs);
File.AppendAllText(filePath, Environment.NewLine);
}
}

void LoadStateFromFile(FileInfo fileInfo, bool? clear, ref int? seedFs, ref bool cleanOutput)
{
if (!fileInfo.Exists) return;
var state = File.ReadAllText(fileInfo.FullName).Split("|");
if (state.Length != 2) return;
seedFs ??= int.TryParse(state[0], out var seed) ? seed : seedFs;
_logger.LogInformation($"Seeding with {seedFs} from previous run {fileInfo.FullName}");
cleanOutput = clear ?? false;
}
}
Loading

0 comments on commit d54bc3a

Please sign in to comment.