diff --git a/src/Elastic.Markdown/Exporters/LlmMarkdownExporter.cs b/src/Elastic.Markdown/Exporters/LlmMarkdownExporter.cs index 7af879b4e..053f756fa 100644 --- a/src/Elastic.Markdown/Exporters/LlmMarkdownExporter.cs +++ b/src/Elastic.Markdown/Exporters/LlmMarkdownExporter.cs @@ -54,27 +54,37 @@ public class LlmMarkdownExporter(bool branded = false) : IMarkdownExporter public async ValueTask FinishExportAsync(IDirectoryInfo outputFolder, Cancel ctx) { + var fs = outputFolder.FileSystem; + if (!outputFolder.Exists) + outputFolder.Create(); + var outputDirectory = outputFolder.FullName; - var zipPath = Path.Join(outputDirectory, "llm.zip"); + var zipPath = fs.Path.Join(outputDirectory, "llm.zip"); // Create the llms.txt file; omit Elastic boilerplate for branded builds - var llmsTxt = Path.Join(outputDirectory, "llms.txt"); - await outputFolder.FileSystem.File.WriteAllTextAsync(llmsTxt, branded ? string.Empty : LlmsTxtTemplate, ctx); - - using var zip = ZipFile.Open(zipPath, ZipArchiveMode.Create); - var llmsTxtRelativePath = Path.GetRelativePath(outputDirectory, llmsTxt); - _ = zip.CreateEntryFromFile(llmsTxt, llmsTxtRelativePath); + var llmsTxt = fs.FileInfo.New(fs.Path.Join(outputDirectory, "llms.txt")); + await fs.File.WriteAllTextAsync(llmsTxt.FullName, branded ? string.Empty : LlmsTxtTemplate, ctx); - var markdownFiles = Directory.GetFiles(outputDirectory, "*.md", SearchOption.AllDirectories); + await using var zipStream = fs.File.Create(zipPath); + using var zip = new ZipArchive(zipStream, ZipArchiveMode.Create); + await AddFileAsync(zip, llmsTxt, llmsTxt.Name, ctx); - foreach (var file in markdownFiles) + foreach (var file in outputFolder.EnumerateFiles("*.md", SearchOption.AllDirectories)) { - var relativePath = Path.GetRelativePath(outputDirectory, file); - _ = zip.CreateEntryFromFile(file, relativePath); + var relativePath = fs.Path.GetRelativePath(outputDirectory, file.FullName).Replace('\\', '/'); + await AddFileAsync(zip, file, relativePath, ctx); } return true; } + private static async Task AddFileAsync(ZipArchive archive, IFileInfo file, string entryName, Cancel ctx) + { + var entry = archive.CreateEntry(entryName, CompressionLevel.Optimal); + await using var entryStream = entry.Open(); + await using var sourceStream = file.FileSystem.File.OpenRead(file.FullName); + await sourceStream.CopyToAsync(entryStream, ctx); + } + public async ValueTask ExportAsync(MarkdownExportFileContext fileContext, Cancel ctx) { var fs = fileContext.BuildContext.WriteFileSystem; diff --git a/tests/Elastic.Markdown.Tests/Exporters/LlmMarkdownExporterTests.cs b/tests/Elastic.Markdown.Tests/Exporters/LlmMarkdownExporterTests.cs new file mode 100644 index 000000000..d72f699bd --- /dev/null +++ b/tests/Elastic.Markdown.Tests/Exporters/LlmMarkdownExporterTests.cs @@ -0,0 +1,33 @@ +// 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.IO.Abstractions.TestingHelpers; +using System.IO.Compression; +using AwesomeAssertions; +using Elastic.Markdown.Exporters; + +namespace Elastic.Markdown.Tests.Exporters; + +public class LlmMarkdownExporterTests +{ + [Fact] + public async Task FinishExportAsync_InMemoryFileSystem_CreatesArchiveFromInMemoryFiles() + { + const string outputPath = "/repo/.artifacts/docs/html"; + var fileSystem = new MockFileSystem(new Dictionary + { + [$"{outputPath}/guide/page.md"] = new("# Page") + }); + var outputFolder = fileSystem.DirectoryInfo.New(outputPath); + var exporter = new LlmMarkdownExporter(); + + var result = await exporter.FinishExportAsync(outputFolder, TestContext.Current.CancellationToken); + + result.Should().BeTrue(); + fileSystem.File.Exists($"{outputPath}/llm.zip").Should().BeTrue(); + await using var zipStream = fileSystem.File.OpenRead($"{outputPath}/llm.zip"); + using var archive = new ZipArchive(zipStream, ZipArchiveMode.Read); + archive.Entries.Select(entry => entry.FullName).Should().BeEquivalentTo("llms.txt", "guide/page.md"); + } +}