diff --git a/.github/.github.csproj b/.github/.github.csproj index f87d5e5..ea6821a 100644 --- a/.github/.github.csproj +++ b/.github/.github.csproj @@ -1,6 +1,6 @@ - net8.0 + net9.0 False diff --git a/.github/actions/bootstrap/action.yml b/.github/actions/bootstrap/action.yml index aef0072..4066c15 100644 --- a/.github/actions/bootstrap/action.yml +++ b/.github/actions/bootstrap/action.yml @@ -18,6 +18,9 @@ runs: run: | git fetch --prune --unshallow --tags git tag --list + - uses: actions/setup-dotnet@v4 + with: + global-json-file: global.json - id: dotnet shell: bash diff --git a/Dockerfile b/Dockerfile deleted file mode 100644 index baa54e3..0000000 --- a/Dockerfile +++ /dev/null @@ -1,55 +0,0 @@ -FROM mcr.microsoft.com/dotnet/runtime-deps:8.0-alpine AS base -WORKDIR /app -EXPOSE 8080 -EXPOSE 443 - -FROM --platform=$BUILDPLATFORM mcr.microsoft.com/dotnet/sdk:8.0-alpine AS build -ARG TARGETARCH -ARG BUILDPLATFORM - -# Install NativeAOT build prerequisites -#RUN apt-get update \ -# && apt-get install -y --no-install-recommends \ -# clang zlib1g-dev - -WORKDIR /src -COPY ["src/Elastic.Markdown/Elastic.Markdown.csproj", "src/Elastic.Markdown/Elastic.Markdown.csproj"] -COPY ["src/docs-builder/docs-builder.csproj", "src/docs-builder/docs-builder.csproj"] -COPY ["docs/docs.csproj", "docs/docs.csproj"] -COPY "build/build.csproj" "build/build.csproj" -COPY ".github/.github.csproj" ".github/.github.csproj" -COPY "tests/Elastic.Markdown.Tests/Elastic.Markdown.Tests.csproj" "tests/Elastic.Markdown.Tests/Elastic.Markdown.Tests.csproj" -COPY ["docs-builder.sln", "docs-builder.sln"] -RUN dotnet restore "docs-builder.sln" -COPY . . -WORKDIR "/src" -RUN dotnet build "src/docs-builder/docs-builder.csproj" -c Release -o /app/build -a $TARGETARCH - -FROM build AS publish -RUN dotnet publish "src/docs-builder/docs-builder.csproj" -c Release -o /app/publish \ - #--runtime alpine-x64 \ - --self-contained true \ - /p:PublishTrimmed=true \ - /p:PublishSingleFile=true \ - /p:PublishAot=false \ - -a $TARGETARCH - -FROM --platform=$BUILDPLATFORM base AS final -ARG TARGETARCH -ARG BUILDPLATFORM - -# create a new user and change directory ownership -RUN adduser --disabled-password \ - --home /app \ - --gecos '' dotnetuser && chown -R dotnetuser /app - -# impersonate into the new user -USER dotnetuser -WORKDIR /app - -COPY NOTICE.txt . -COPY LICENSE.txt . -COPY --from=publish /app/publish . -CMD chmod +x docs-builder - -ENTRYPOINT ["./docs-builder"] diff --git a/build/Program.cs b/build/Program.cs index c882713..1811995 100644 --- a/build/Program.cs +++ b/build/Program.cs @@ -22,14 +22,12 @@ { var source = "src/docs-builder/docs-builder.csproj"; await $""" - dotnet publish {source} -c Release -o .artifacts/publish \ - --self-contained true /p:PublishTrimmed=true /p:PublishSingleFile=false /p:PublishAot=true + dotnet publish {source} """; var generatorSource = "src/docs-generator/docs-generator.csproj"; await $""" - dotnet publish {generatorSource} -c Release -o .artifacts/publish \ - --self-contained true /p:PublishTrimmed=true /p:PublishSingleFile=false /p:PublishAot=true + dotnet publish {generatorSource} """; }); diff --git a/build/build.csproj b/build/build.csproj index 7216e06..fdd0dbf 100644 --- a/build/build.csproj +++ b/build/build.csproj @@ -1,6 +1,6 @@ - net8.0 + net9.0 Exe $(NoWarn);NU1701 false diff --git a/docs-builder.sln b/docs-builder.sln index db20c25..bbe6e10 100644 --- a/docs-builder.sln +++ b/docs-builder.sln @@ -7,12 +7,11 @@ EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "root", "root", "{94AC66E5-38C3-4625-92D3-08990FD5F429}" ProjectSection(SolutionItems) = preProject Directory.Build.props = Directory.Build.props - Dockerfile = Dockerfile README.md = README.md - run.sh = run.sh build.bat = build.bat build.sh = build.sh dotnet-tools.json = dotnet-tools.json + global.json = global.json EndProjectSection EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{BE6011CC-1200-4957-B01F-FCCA10C5CF5A}" diff --git a/docs/docs.csproj b/docs/docs.csproj index 568bf7d..36ca014 100644 --- a/docs/docs.csproj +++ b/docs/docs.csproj @@ -1,6 +1,6 @@ - net8.0 + net9.0 False diff --git a/docs/source/index.md b/docs/source/index.md index 7495ae5..bc23f96 100644 --- a/docs/source/index.md +++ b/docs/source/index.md @@ -9,10 +9,11 @@ This repository is host to: * *`docs-builder`* command line tool to generate single doc-sets (13mb native code, no dependencies) * *`docs-assembler`* command line tool to assemble all the doc sets. (IN PROGRESS) * `elastic/docs-builder@main` Github Action to build and validate a repositories documentation +* *`docs-generator`* command line tool to deterministically generate a docset and incremental updates to generated content ## Command line interface -``` +```bash $ docs-builder --help Usage: [command] [options...] [-h|--help] [--version] @@ -130,16 +131,13 @@ https://github.com/elastic/{your-repository}/settings/pages ## Run without docker If you have dotnet 8 installed you can use its CLI to publish a self-contained `docs-builder` native code -binary. (On my M2 Pro mac the binary is currently 13mb) +binary. (On my M2 Pro mac the binary is currently 16mb) ```bash -$ dotnet publish "src/docs-builder/docs-builder.csproj" -c Release -o .artifacts/publish \ - --self-contained true /p:PublishTrimmed=true /p:PublishSingleFile=false /p:PublishAot=true -a arm64 +$ dotnet publish "src/docs-builder/docs-builder.csproj" ``` -**Note**: `-a` should be the machines CPU architecture - -The resulting binary `./.artifacts/publish/docs-builder` will run on machines without .NET installed +The resulting binary `./.artifacts/publish/docs-builder/release/docs-builder` will run on machines without .NET installed # Performance diff --git a/global.json b/global.json new file mode 100644 index 0000000..00b67ca --- /dev/null +++ b/global.json @@ -0,0 +1,7 @@ +{ + "sdk": { + "version": "9.0.100", + "rollForward": "latestFeature", + "allowPrerelease": false + } +} \ No newline at end of file diff --git a/run.sh b/run.sh deleted file mode 100755 index 5a9353c..0000000 --- a/run.sh +++ /dev/null @@ -1,44 +0,0 @@ -#!/bin/sh - -set -euo pipefail - -# guess OS_TYPE if not provided -OS_TYPE=${OS_TYPE:-} -if [ -z "$OS_TYPE" ]; then - case "$(uname -s | tr '[:upper:]' '[:lower:]')" in - cygwin_nt*|mingw*|msys_nt*) - OS_TYPE="windows" - ;; - linux*) - OS_TYPE="linux" - ;; - darwin*) - OS_TYPE="linux" - ;; - esac -fi - -ARCHITECTURE=${ARCHITECTURE:-} -# guess OS architecture if not provided -if [ -z "$ARCHITECTURE" ]; then - case $(uname -m) in - x86_64) ARCHITECTURE="x64" ;; - aarch64) ARCHITECTURE="arm64" ;; - arm64) ARCHITECTURE="arm64" ;; - esac -fi - -docker build --platform="${OS_TYPE}/${ARCHITECTURE}" -f Dockerfile -t docs-builder-${ARCHITECTURE}:latest . -echo "Dockerfile build" - -if test -d "../markitpy-poc/docs/source/"; then - echo "Auto mounting markitpy-poc docs" - docker run -v "${PWD}/../markitpy-poc/docs/source/:/app/markitpy-poc/docs/source/" -v "${PWD}/docs/:/app/docs/" -v "${PWD}/.artifacts/:/app/.artifacts/" \ - -p 8080:8080 \ - --entrypoint "./docs-builder" "docs-builder-${ARCHITECTURE}:latest" "$@" -else - docker run -v "${PWD}/docs/:/app/docs/" -v "${PWD}/.artifacts/:/app/.artifacts/" \ - -p 8080:8080 \ - --entrypoint "./docs-builder" "docs-builder-${ARCHITECTURE}:latest" "$@" -fi - diff --git a/src/Elastic.Markdown/DocumentationGenerator.cs b/src/Elastic.Markdown/DocumentationGenerator.cs index df0955b..31797eb 100644 --- a/src/Elastic.Markdown/DocumentationGenerator.cs +++ b/src/Elastic.Markdown/DocumentationGenerator.cs @@ -21,6 +21,7 @@ public record GenerationState { [JsonPropertyName("last_seen_changes")] public required DateTimeOffset LastSeenChanges { get; init; } + [JsonPropertyName("invalid_files")] public required string[] InvalidFiles { get; init; } = []; diff --git a/src/Elastic.Markdown/Elastic.Markdown.csproj b/src/Elastic.Markdown/Elastic.Markdown.csproj index f616d87..3572eba 100644 --- a/src/Elastic.Markdown/Elastic.Markdown.csproj +++ b/src/Elastic.Markdown/Elastic.Markdown.csproj @@ -1,7 +1,7 @@  - net8.0 + net9.0 enable enable true @@ -16,7 +16,7 @@ - + diff --git a/src/Elastic.Markdown/IO/GitConfiguration.cs b/src/Elastic.Markdown/IO/GitConfiguration.cs index be83875..510f04f 100644 --- a/src/Elastic.Markdown/IO/GitConfiguration.cs +++ b/src/Elastic.Markdown/IO/GitConfiguration.cs @@ -4,8 +4,7 @@ using System.IO.Abstractions; using System.Text.Json.Serialization; -using IniParser; -using IniParser.Model; +using SoftCircuits.IniFileParser; namespace Elastic.Markdown.IO; @@ -42,15 +41,16 @@ public static GitConfiguration Create(IFileSystem fileSystem) else branch = "detached/head"; - var ini = new FileIniDataParser(); + var ini = new IniFile(); using var stream = gitConfig.OpenRead(); using var streamReader = new StreamReader(stream); - var config = ini.ReadData(streamReader); - var remote = BranchTrackingRemote(branch, config); + ini.Load(streamReader); + + var remote = BranchTrackingRemote(branch, ini); if (string.IsNullOrEmpty(remote)) - remote = BranchTrackingRemote("main", config); + remote = BranchTrackingRemote("main", ini); if (string.IsNullOrEmpty(remote)) - remote = BranchTrackingRemote("master", config); + remote = BranchTrackingRemote("master", ini); if (string.IsNullOrEmpty(remote)) remote = Environment.GetEnvironmentVariable("GITHUB_REPOSITORY") ?? "elastic/docs-builder-unknown"; @@ -61,11 +61,19 @@ public static GitConfiguration Create(IFileSystem fileSystem) string Read(string path) => fileSystem.File.ReadAllText(Git(path).FullName).Trim(Environment.NewLine.ToCharArray()); - string BranchTrackingRemote(string b, IniData c) + string BranchTrackingRemote(string b, IniFile c) { - var remoteName = c[$"branch \"{b}\""]["remote"]; - remote = c[$"remote \"{remoteName}\""]["url"]; - return remote; + var sections = c.GetSections(); + var branchSection = $"branch \"{b}\""; + if (!sections.Contains(branchSection)) + return string.Empty; + + var remoteName = ini.GetSetting(branchSection, "remote"); + + var remoteSection = $"remote \"{remoteName}\""; + + remote = ini.GetSetting(remoteSection, "url"); + return remote ?? string.Empty; } } } diff --git a/src/Elastic.Markdown/IO/LinkReference.cs b/src/Elastic.Markdown/IO/LinkReference.cs index 576215a..da42a79 100644 --- a/src/Elastic.Markdown/IO/LinkReference.cs +++ b/src/Elastic.Markdown/IO/LinkReference.cs @@ -2,9 +2,7 @@ // 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; using System.Text.Json.Serialization; -using IniParser; namespace Elastic.Markdown.IO; diff --git a/src/docs-builder/Cli/ArgsFilter.cs b/src/docs-builder/Cli/ArgsFilter.cs deleted file mode 100644 index bde2ff6..0000000 --- a/src/docs-builder/Cli/ArgsFilter.cs +++ /dev/null @@ -1,34 +0,0 @@ -// 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.Builder.Cli; - -/// -/// 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 -/// -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 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; - } - } - } -} diff --git a/src/docs-builder/ConsoleApp.cs b/src/docs-builder/ConsoleApp.cs new file mode 100644 index 0000000..0232566 --- /dev/null +++ b/src/docs-builder/ConsoleApp.cs @@ -0,0 +1,16 @@ +// 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.Diagnostics.CodeAnalysis; + +// this is a temporary workaround for https://github.com/Cysharp/ConsoleAppFramework/issues/154 +// it generates code that is not AOT safe, but it's in a code path we don't use. +// therefor I am comfortable running with this band-aid for now + +// ReSharper disable once CheckNamespace +namespace ConsoleAppFramework; + +[UnconditionalSuppressMessage("AssemblyLoadTrimming", "IL2026:RequiresUnreferencedCode", Justification = "Manually verified")] +[UnconditionalSuppressMessage("AssemblyLoadTrimming", "IL3050:RequiresDynamicCode", Justification = "Manually verified")] +internal static partial class ConsoleApp; diff --git a/src/docs-builder/Http/DocumentationWebHost.cs b/src/docs-builder/Http/DocumentationWebHost.cs index 656e9bb..416b3f2 100644 --- a/src/docs-builder/Http/DocumentationWebHost.cs +++ b/src/docs-builder/Http/DocumentationWebHost.cs @@ -32,7 +32,7 @@ public DocumentationWebHost(string? path, ILoggerFactory logger, IFileSystem fil { Collector = new ConsoleDiagnosticsCollector(logger) }; - builder.Services.AddLiveReload(s => + builder.Services.AddAotLiveReload(s => { s.FolderToMonitor = context.SourcePath.FullName; s.ClientFileExtensions = ".md,.yml"; diff --git a/src/docs-builder/Http/LiveReload.cs b/src/docs-builder/Http/LiveReload.cs new file mode 100644 index 0000000..904e37d --- /dev/null +++ b/src/docs-builder/Http/LiveReload.cs @@ -0,0 +1,57 @@ +// 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.Diagnostics.CodeAnalysis; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; + +namespace Westwind.AspNetCore.LiveReload; + +[UnconditionalSuppressMessage("AssemblyLoadTrimming", "IL2026:RequiresUnreferencedCode", Justification = "Manually verified")] +[UnconditionalSuppressMessage("AssemblyLoadTrimming", "IL3050:RequiresDynamicCode", Justification = "Manually verified")] +public static class LiveReloadMiddlewareExtensions +{ + + public static IServiceCollection AddAotLiveReload(this IServiceCollection services, + Action configAction) + { + + var provider = services.BuildServiceProvider(); + var configuration = provider.GetService(); + + var config = new LiveReloadConfiguration(); + configuration!.Bind("LiveReload", config); + + LiveReloadConfiguration.Current = config; + + if (config.LiveReloadEnabled) + { + var env = provider.GetService(); + if (string.IsNullOrEmpty(config.FolderToMonitor)) + { + config.FolderToMonitor = env!.ContentRootPath; + } + else if (config.FolderToMonitor.StartsWith("~")) + { + if (config.FolderToMonitor.Length > 1) + { + var folder = config.FolderToMonitor.Substring(1); + if (folder.StartsWith('/') || folder.StartsWith("\\")) + folder = folder.Substring(1); + config.FolderToMonitor = Path.Combine(env!.ContentRootPath, folder); + config.FolderToMonitor = Path.GetFullPath(config.FolderToMonitor); + } + else + config.FolderToMonitor = env!.ContentRootPath; + } + + configAction.Invoke(config); + + LiveReloadConfiguration.Current = config; + } + + return services; + } +} diff --git a/src/docs-builder/Program.cs b/src/docs-builder/Program.cs index 25d9d38..8f0c050 100644 --- a/src/docs-builder/Program.cs +++ b/src/docs-builder/Program.cs @@ -10,8 +10,6 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; -var arguments = Arguments.Filter(args); - var services = new ServiceCollection(); services.AddGitHubActionsCore(); services.AddLogging(x => @@ -33,11 +31,12 @@ await using var serviceProvider = services.BuildServiceProvider(); var logger = serviceProvider.GetRequiredService>(); ConsoleApp.ServiceProvider = serviceProvider; -if (!arguments.IsHelp) +var isHelp = args.Contains("-h") || args.Contains("--help"); +if (!isHelp) ConsoleApp.Log = msg => logger.LogInformation(msg); ConsoleApp.LogError = msg => logger.LogError(msg); var app = ConsoleApp.Create(); app.Add(); -await app.RunAsync(arguments.Args).ConfigureAwait(false); +await app.RunAsync(args).ConfigureAwait(false); diff --git a/src/docs-builder/docs-builder.csproj b/src/docs-builder/docs-builder.csproj index 945726a..17d1bd6 100644 --- a/src/docs-builder/docs-builder.csproj +++ b/src/docs-builder/docs-builder.csproj @@ -1,26 +1,25 @@  - net8.0 + net9.0 Exe docs-builder Documentation.Builder true + true - true true $(InterceptorsPreviewNamespaces);Microsoft.AspNetCore.Http.Generated true + true + true true - - + false - + all runtime; build; native; contentfiles; analyzers; buildtransitive @@ -28,7 +27,6 @@ - diff --git a/src/docs-generator/Cli/ArgsFilter.cs b/src/docs-generator/Cli/ArgsFilter.cs deleted file mode 100644 index 896987e..0000000 --- a/src/docs-generator/Cli/ArgsFilter.cs +++ /dev/null @@ -1,34 +0,0 @@ -// 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; - -/// -/// 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 -/// -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 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; - } - } - } -} diff --git a/src/docs-generator/Program.cs b/src/docs-generator/Program.cs index 7a73f7f..4faee4a 100644 --- a/src/docs-generator/Program.cs +++ b/src/docs-generator/Program.cs @@ -14,9 +14,6 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; - -var arguments = Arguments.Filter(args); - var services = new ServiceCollection(); services.AddGitHubActionsCore(); services.AddLogging(x => @@ -35,11 +32,12 @@ await using var serviceProvider = services.BuildServiceProvider(); var logger = serviceProvider.GetRequiredService>(); ConsoleApp.ServiceProvider = serviceProvider; -if (!arguments.IsHelp) +var isHelp = args.Contains("-h") || args.Contains("--help"); +if (!isHelp) ConsoleApp.Log = msg => logger.LogInformation(msg); ConsoleApp.LogError = msg => logger.LogError(msg); var app = ConsoleApp.Create(); app.Add(); -await app.RunAsync(arguments.Args).ConfigureAwait(false); +await app.RunAsync(args).ConfigureAwait(false); diff --git a/src/docs-generator/docs-generator.csproj b/src/docs-generator/docs-generator.csproj index edee697..bc63f82 100644 --- a/src/docs-generator/docs-generator.csproj +++ b/src/docs-generator/docs-generator.csproj @@ -1,7 +1,7 @@  - net8.0 + net9.0 Exe docs-generator Documentation.Generator @@ -19,7 +19,7 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/tests/Elastic.Markdown.Tests/Elastic.Markdown.Tests.csproj b/tests/Elastic.Markdown.Tests/Elastic.Markdown.Tests.csproj index eb53a99..f7c7fc1 100644 --- a/tests/Elastic.Markdown.Tests/Elastic.Markdown.Tests.csproj +++ b/tests/Elastic.Markdown.Tests/Elastic.Markdown.Tests.csproj @@ -1,7 +1,7 @@ - net8.0 + net9.0 enable enable