diff --git a/docs/syntax/applies.md b/docs/syntax/applies.md index b15cbe01c..d87aca295 100644 --- a/docs/syntax/applies.md +++ b/docs/syntax/applies.md @@ -1,34 +1,47 @@ # Applies to -Starting with Elastic Stack 9.0, ECE 4.0, and ECK 3.0, documentation follows a [cumulative approach](../contribute/index.md#cumulative-docs): instead of creating separate pages for each product and release, we update a single page with product- and version-specific details over time. +Starting with Elastic Stack 9.0, ECE 4.0, and ECK 3.0, documentation follows +a [cumulative approach](../contribute/index.md#cumulative-docs): instead of creating separate pages for each product and +release, we update a single page with product- and version-specific details over time. To support this, source files use a tagging system to indicate: + * Which Elastic products and deployment models the content applies to. * When a feature changes state relative to the base version. -This is what the `applies_to` metadata is for. It can be used at the page, section, or inline level to specify applicability with precision. +This is what the `applies_to` metadata is for. It can be used at the page, section, or inline level to specify +applicability with precision. ## When and where to use `applies_to` The `applies_to` metadata can be added at different levels in the documentation: -* [Page-level](#page-annotations) metadata is **mandatory** and must be included in the frontmatter. This defines the overall applicability of the page across products, deployments, and environments. -* [Section-level](#section-annotations) annotations allow you to specify different applicability for individual sections when only part of a page varies between products or versions. -* [Inline](#inline-annotations) annotations allow fine-grained annotations within paragraphs or definition lists. This is useful for highlighting the applicability of specific phrases, sentences, or properties without disrupting the surrounding content. +* [Page-level](#page-annotations) metadata is **mandatory** and must be included in the frontmatter. This defines the + overall applicability of the page across products, deployments, and environments. +* [Section-level](#section-annotations) annotations allow you to specify different applicability for individual sections + when only part of a page varies between products or versions. +* [Inline](#inline-annotations) annotations allow fine-grained annotations within paragraphs or definition lists. This + is useful for highlighting the applicability of specific phrases, sentences, or properties without disrupting the + surrounding content. ### Do’s and don’ts -✅ Use `applies_to` tags when features change state (`preview`, `beta`, `ga`, `deprecated`, `removed`) or when availability differs across deployments and environments. +✅ Use `applies_to` tags when features change state (`preview`, `beta`, `ga`, `deprecated`, `removed`) or when +availability differs across deployments and environments. -✅ Use `applies_to` tags to indicate which product or deployment type the content applies to. This is mandatory for every page. +✅ Use `applies_to` tags to indicate which product or deployment type the content applies to. This is mandatory for every +page. ✅ Use `applies_to` tags when features change state in a specific update or release. -❌ Don't tag content-only changes like typos, formatting, or documentation updates that don't reflect feature lifecycle changes. +❌ Don't tag content-only changes like typos, formatting, or documentation updates that don't reflect feature lifecycle +changes. -❌ You don’t need to tag every section or paragraph. Only do so if the context or applicability changes from what has been established earlier. +❌ You don’t need to tag every section or paragraph. Only do so if the context or applicability changes from what has +been established earlier. -❌ If the product is not versioned (meaning all users are always on the latest version, like in serverless or cloud), you do not need to tag a new GA feature. +❌ If the product is not versioned (meaning all users are always on the latest version, like in serverless or cloud), you +do not need to tag a new GA feature. ## Syntax @@ -53,12 +66,12 @@ Note that a key without any value doesn't show any badge in the output. `applies_to` accepts the following lifecycle states: - * `preview` - * `beta` - * `deprecated` - * `removed` - * `unavailable` - * `ga` +* `preview` +* `beta` +* `deprecated` +* `removed` +* `unavailable` +* `ga` ### Version @@ -72,6 +85,7 @@ applies_to: deployment: ece: deprecated 9.2, removed 9.8 ``` + Unversioned products use `lifecycle` tags without a version: ``` @@ -86,9 +100,13 @@ applies_to: ### Lifecycle examples #### Unversioned products -Unversioned products don't follow a fixed versioning scheme and are released a lot more often than versioned products. All users are using the same version of this product. + +Unversioned products don't follow a fixed versioning scheme and are released a lot more often than versioned products. +All users are using the same version of this product. + * When a change is released in `ga`, it **doesn’t need any specific tagging**. -* When a change is introduced as preview or beta, use `preview` or `beta` as value for the corresponding key within the `applies_to`: +* When a change is introduced as preview or beta, use `preview` or `beta` as value for the corresponding key within the + `applies_to`: ``` --- @@ -106,8 +124,10 @@ Unversioned products don't follow a fixed versioning scheme and are released a l --- ``` -* When a change removes a feature, remove the content. -**Exception:** If the content also applies to another context (for example a feature is removed in both Kibana 9.x and Serverless), then it must be kept for any user reading the page that may be using a version of Kibana prior to the removal. For example: +* When a change removes a feature, remove the content. + **Exception:** If the content also applies to another context (for example a feature is removed in both Kibana 9.x and + Serverless), then it must be kept for any user reading the page that may be using a version of Kibana prior to the + removal. For example: ``` --- @@ -128,7 +148,8 @@ Unversioned products don't follow a fixed versioning scheme and are released a l --- ``` -* When a change is introduced as preview or beta, use `preview` or `beta` as value for the corresponding key within the `applies_to`: +* When a change is introduced as preview or beta, use `preview` or `beta` as value for the corresponding key within the + `applies_to`: ``` --- @@ -147,7 +168,9 @@ Unversioned products don't follow a fixed versioning scheme and are released a l --- ``` -* When a change removes a feature, any user reading the page that may be using a version of Kibana prior to the removal must be aware that the feature is still available to them. For that reason, we do not remove the content, and instead mark the feature as removed: +* When a change removes a feature, any user reading the page that may be using a version of Kibana prior to the removal + must be aware that the feature is still available to them. For that reason, we do not remove the content, and instead + mark the feature as removed: ``` --- @@ -158,7 +181,8 @@ Unversioned products don't follow a fixed versioning scheme and are released a l #### Identify multiple states for the same content -A feature is deprecated in ECE 4.0 and is removed in 4.8. At the same time, it has already been removed in Elastic Cloud Hosted: +A feature is deprecated in ECE 4.0 and is removed in 4.8. At the same time, it has already been removed in Elastic Cloud +Hosted: ``` --- @@ -171,12 +195,16 @@ applies_to: ### Page annotations -All documentation pages **must** include an `applies_to` tag in the YAML frontmatter. Use YAML frontmatter to indicate each deployment targets availability and lifecycle status. For a complete list of supported keys and values, see the [frontmatter syntax guide](./frontmatter.md). +All documentation pages **must** include an `applies_to` tag in the YAML frontmatter. Use YAML frontmatter to indicate +each deployment targets availability and lifecycle status. For a complete list of supported keys and values, see +the [frontmatter syntax guide](./frontmatter.md). #### Page annotation examples There are 3 typical scenarios to start from: -1. The documentation set or page is primarily about using or interacting with Elastic Stack components or the Serverless UI: + +1. The documentation set or page is primarily about using or interacting with Elastic Stack components or the Serverless + UI: ```yaml --- @@ -190,7 +218,8 @@ There are 3 typical scenarios to start from: --- ``` -2. The documentation set or page is primarily about orchestrating, deploying or configuring an installation (only include relevant keys): +2. The documentation set or page is primarily about orchestrating, deploying or configuring an installation (only + include relevant keys): ```yaml --- @@ -242,8 +271,8 @@ of the section further. the `{applies_to}` directive **MUST** be preceded by a heading directly. ::: - -Note that this directive requires triple backticks since its content is literal. See also [](index.md#literal-directives) +Note that this directive requires triple backticks since its content is literal. See +also [](index.md#literal-directives) ````markdown ```{applies_to} @@ -263,7 +292,8 @@ This will allow the YAML inside the `{applies_to}` directive to be fully highlig #### Section annotation examples -1. The whole page is generally applicable to Elastic Stack 9.0 and to Serverless, but one specific section isn’t applicable to Serverless (and there is no alternative for it): +1. The whole page is generally applicable to Elastic Stack 9.0 and to Serverless, but one specific section isn’t + applicable to Serverless (and there is no alternative for it): ````markdown ## Configure a space-level landing page [space-landing-page] @@ -273,7 +303,9 @@ This will allow the YAML inside the `{applies_to}` directive to be fully highlig ``` ```` -2. The whole page is generally applicable to Elastic Cloud Enterprise and Elastic Cloud Hosted, but one specific paragraph only applies to Elastic Cloud Enterprise, and another paragraph explains the same, but for Elastic Cloud Hosted: +2. The whole page is generally applicable to Elastic Cloud Enterprise and Elastic Cloud Hosted, but one specific + paragraph only applies to Elastic Cloud Enterprise, and another paragraph explains the same, but for Elastic Cloud + Hosted: ````markdown ## Secure a deployment [secure-deployment-ech] @@ -292,7 +324,9 @@ This will allow the YAML inside the `{applies_to}` directive to be fully highlig [...] ```` -3. A specific section, paragraph or list item has specific applicability that differs from the context set at the page or section level, and the action is not possible at all for that context (meaning that there is no alternative). For example: +3. A specific section, paragraph or list item has specific applicability that differs from the context set at the page + or section level, and the action is not possible at all for that context (meaning that there is no alternative). For + example: ````markdown --- @@ -320,11 +354,13 @@ Inline applies to can be placed anywhere using the following syntax This can live inline {applies_to}`section: [version]` ``` -An inline version example would be {applies_to}`stack: beta 9.1` this allows you to target elements more concretely visually. +An inline version example would be {applies_to}`stack: beta 9.1` this allows you to target elements more concretely +visually. #### Inline annotation examples -1. The whole page is generally applicable to Elastic Stack 9.0 and to Serverless, but one specific section isn’t applicable to Serverless (and there is no alternative): +1. The whole page is generally applicable to Elastic Stack 9.0 and to Serverless, but one specific section isn’t + applicable to Serverless (and there is no alternative): ````markdown **Spaces** let you organize your content and users according to your needs. @@ -333,7 +369,8 @@ An inline version example would be {applies_to}`stack: beta 9.1` this allows you - {applies_to}`stack: ga` {applies_to}`serverless: unavailable` Each space has its own navigation, called solution view. ```` -A specialized `{preview}` role exist to quickly mark something as a technical preview. It takes a required version number +A specialized `{preview}` role exist to quickly mark something as a technical preview. It takes a required version +number as argument. ```markdown @@ -361,6 +398,70 @@ applies_to: elasticsearch: observability: product: + ecctl: + curator: + apm_agent_android: + apm_agent_dotnet: + apm_agent_go: + apm_agent_ios: + apm_agent_java: + apm_agent_node: + apm_agent_php: + apm_agent_python: + apm_agent_ruby: + apm_agent_rum: + edot_ios: + edot_android: + edot_dotnet: + edot_java: + edot_node: + edot_php: + edot_python: --- ``` + This allows you to annotate various facets as defined in [](../migration/versioning.md) + +## Inline Examples + +### Stack + +| `applies_to` | result | +|--------------------------------------------|--------------------------------------| +| `` {applies_to}`stack: ` `` | {applies_to}`stack: ` | +| `` {applies_to}`stack: preview` `` | {applies_to}`stack: preview` | +| `` {applies_to}`stack: preview 8.18` `` | {applies_to}`stack: preview 8.18` | +| `` {applies_to}`stack: preview 9.0` `` | {applies_to}`stack: preview 9.0` | +| `` {applies_to}`stack: preview 9.1` `` | {applies_to}`stack: preview 9.1` | +| `` {applies_to}`stack: preview 99.0` `` | {applies_to}`stack: preview 99.0` | +| `` {applies_to}`stack: ga` `` | {applies_to}`stack: ga` | +| `` {applies_to}`stack: ga 8.18` `` | {applies_to}`stack: ga 8.18` | +| `` {applies_to}`stack: ga 9.0` `` | {applies_to}`stack: ga 9.0` | +| `` {applies_to}`stack: ga 9.1` `` | {applies_to}`stack: ga 9.1` | +| `` {applies_to}`stack: ga 99.0` `` | {applies_to}`stack: ga 99.0` | +| `` {applies_to}`stack: beta` `` | {applies_to}`stack: beta` | +| `` {applies_to}`stack: beta 8.18` `` | {applies_to}`stack: beta 8.18` | +| `` {applies_to}`stack: beta 9.0` `` | {applies_to}`stack: beta 9.0` | +| `` {applies_to}`stack: beta 9.1` `` | {applies_to}`stack: beta 9.1` | +| `` {applies_to}`stack: beta 99.0` `` | {applies_to}`stack: beta 99.0` | +| `` {applies_to}`stack: deprecated` `` | {applies_to}`stack: deprecated` | +| `` {applies_to}`stack: deprecated 8.18` `` | {applies_to}`stack: deprecated 8.18` | +| `` {applies_to}`stack: deprecated 9.0` `` | {applies_to}`stack: deprecated 9.0` | +| `` {applies_to}`stack: deprecated 9.1` `` | {applies_to}`stack: deprecated 9.1` | +| `` {applies_to}`stack: deprecated 99.0` `` | {applies_to}`stack: deprecated 99.0` | +| `` {applies_to}`stack: removed` `` | {applies_to}`stack: removed` | +| `` {applies_to}`stack: removed 8.18` `` | {applies_to}`stack: removed 8.18` | +| `` {applies_to}`stack: removed 9.0` `` | {applies_to}`stack: removed 9.0` | +| `` {applies_to}`stack: removed 9.1` `` | {applies_to}`stack: removed 9.1` | +| `` {applies_to}`stack: removed 99.0` `` | {applies_to}`stack: removed 99.0` | + +### Serverless + +| `applies_to` | result | +|-------------------------------------------------|-------------------------------------------| +| `` {applies_to}`serverless: ` `` | {applies_to}`serverless: ` | +| `` {applies_to}`serverless: preview` `` | {applies_to}`serverless: preview` | +| `` {applies_to}`serverless: ga` `` | {applies_to}`serverless: ga` | +| `` {applies_to}`serverless: beta` `` | {applies_to}`serverless: beta` | +| `` {applies_to}`serverless: deprecated` `` | {applies_to}`serverless: deprecated` | +| `` {applies_to}`serverless: removed` `` | {applies_to}`serverless: removed` | diff --git a/docs/syntax/stepper.md b/docs/syntax/stepper.md index 8b30c92e0..b9c5e1c6e 100644 --- a/docs/syntax/stepper.md +++ b/docs/syntax/stepper.md @@ -20,6 +20,7 @@ npm install :::: ::::{step} Build +### Build Then build the project. ```shell npm run build diff --git a/docs/testing/req.md b/docs/testing/req.md index a96c80e2f..f0667c4a5 100644 --- a/docs/testing/req.md +++ b/docs/testing/req.md @@ -1,39 +1,5 @@ ---- -applies_to: - stack: ga 9.1 ---- - # Requirements -Current version: **9.0.0** - -| `applies_to` | result | -|--------------------------------------------|--------------------------------------| -| `` {applies_to}`stack: preview` `` | {applies_to}`stack: preview` | -| `` {applies_to}`stack: preview 8.18` `` | {applies_to}`stack: preview 8.18` | -| `` {applies_to}`stack: preview 9.0` `` | {applies_to}`stack: preview 9.0` | -| `` {applies_to}`stack: preview 9.1` `` | {applies_to}`stack: preview 9.1` | -| `` {applies_to}`stack: ga` `` | {applies_to}`stack: ga` | -| `` {applies_to}`stack: ga 8.18` `` | {applies_to}`stack: ga 8.18` | -| `` {applies_to}`stack: ga 9.0` `` | {applies_to}`stack: ga 9.0` | -| `` {applies_to}`stack: ga 9.1` `` | {applies_to}`stack: ga 9.1` | -| `` {applies_to}`stack: beta` `` | {applies_to}`stack: beta` | -| `` {applies_to}`stack: beta 8.18` `` | {applies_to}`stack: beta 8.18` | -| `` {applies_to}`stack: beta 9.0` `` | {applies_to}`stack: beta 9.0` | -| `` {applies_to}`stack: beta 9.1` `` | {applies_to}`stack: beta 9.1` | -| `` {applies_to}`stack: deprecated` `` | {applies_to}`stack: deprecated` | -| `` {applies_to}`stack: deprecated 8.18` `` | {applies_to}`stack: deprecated 8.18` | -| `` {applies_to}`stack: deprecated 9.0` `` | {applies_to}`stack: deprecated 9.0` | -| `` {applies_to}`stack: deprecated 9.1` `` | {applies_to}`stack: deprecated 9.1` | -| `` {applies_to}`stack: removed` `` | {applies_to}`stack: removed` | -| `` {applies_to}`stack: removed 8.18` `` | {applies_to}`stack: removed 8.18` | -| `` {applies_to}`stack: removed 9.0` `` | {applies_to}`stack: removed 9.0` | -| `` {applies_to}`stack: removed 9.1` `` | {applies_to}`stack: removed 9.1` | -| `` {applies_to}`stack: ` `` | {applies_to}`stack: ` | - -{applies_to}`stack: deprecated 9.1, removed 9.4` - - To follow this tutorial you will need to install the following components: - An installation of Elasticsearch, based on our hosted [Elastic Cloud](https://www.elastic.co/cloud) service (which includes a free trial period), or a self-hosted service that you run on your own computer. See the Install Elasticsearch section above for installation instructions. @@ -44,3 +10,6 @@ The tutorial assumes that you have no previous knowledge of Elasticsearch or gen - Python development - The [Flask](https://flask.palletsprojects.com/) web framework for Python. - The command prompt or terminal application in your operating system. + + +{applies_to}`ece: removed` diff --git a/src/Elastic.Documentation.Configuration/BuildContext.cs b/src/Elastic.Documentation.Configuration/BuildContext.cs index 5265aa171..29c69062b 100644 --- a/src/Elastic.Documentation.Configuration/BuildContext.cs +++ b/src/Elastic.Documentation.Configuration/BuildContext.cs @@ -6,6 +6,7 @@ using System.Reflection; using Elastic.Documentation.Configuration.Assembler; using Elastic.Documentation.Configuration.Builder; +using Elastic.Documentation.Configuration.Versions; using Elastic.Documentation.Diagnostics; namespace Elastic.Documentation.Configuration; @@ -13,7 +14,8 @@ namespace Elastic.Documentation.Configuration; public record BuildContext : IDocumentationContext { public static string Version { get; } = Assembly.GetExecutingAssembly().GetCustomAttributes() - .FirstOrDefault()?.InformationalVersion ?? "0.0.0"; + .FirstOrDefault()?.InformationalVersion ?? "0.0.0"; + public IFileSystem ReadFileSystem { get; } public IFileSystem WriteFileSystem { get; } @@ -23,6 +25,8 @@ public record BuildContext : IDocumentationContext public ConfigurationFile Configuration { get; } + public VersionsConfiguration VersionsConfig { get; init; } + public IFileInfo ConfigurationPath { get; } public GitCheckoutInformation Git { get; } @@ -60,13 +64,16 @@ public string? UrlPathPrefix init => _urlPathPrefix = value; } - public BuildContext(IDiagnosticsCollector collector, IFileSystem fileSystem) - : this(collector, fileSystem, fileSystem, null, null) { } + public BuildContext(IDiagnosticsCollector collector, IFileSystem fileSystem, VersionsConfiguration versionsConfig) + : this(collector, fileSystem, fileSystem, versionsConfig, null, null) + { + } public BuildContext( IDiagnosticsCollector collector, IFileSystem readFileSystem, IFileSystem writeFileSystem, + VersionsConfiguration versionsConfig, string? source = null, string? output = null, GitCheckoutInformation? gitCheckoutInformation = null @@ -75,6 +82,7 @@ public BuildContext( Collector = collector; ReadFileSystem = readFileSystem; WriteFileSystem = writeFileSystem; + VersionsConfig = versionsConfig; var rootFolder = !string.IsNullOrWhiteSpace(source) ? ReadFileSystem.DirectoryInfo.New(source) @@ -98,5 +106,4 @@ public BuildContext( Enabled = false }; } - } diff --git a/src/Elastic.Documentation.Configuration/Elastic.Documentation.Configuration.csproj b/src/Elastic.Documentation.Configuration/Elastic.Documentation.Configuration.csproj index 5a54184a9..f8bc96be8 100644 --- a/src/Elastic.Documentation.Configuration/Elastic.Documentation.Configuration.csproj +++ b/src/Elastic.Documentation.Configuration/Elastic.Documentation.Configuration.csproj @@ -16,4 +16,8 @@ + + + + diff --git a/src/Elastic.Documentation.Configuration/Serialization/YamlStaticContext.cs b/src/Elastic.Documentation.Configuration/Serialization/YamlStaticContext.cs index 86fab0986..8e9ed0789 100644 --- a/src/Elastic.Documentation.Configuration/Serialization/YamlStaticContext.cs +++ b/src/Elastic.Documentation.Configuration/Serialization/YamlStaticContext.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information using Elastic.Documentation.Configuration.Assembler; +using Elastic.Documentation.Configuration.Versions; using YamlDotNet.Serialization; namespace Elastic.Documentation.Configuration.Serialization; @@ -15,4 +16,6 @@ namespace Elastic.Documentation.Configuration.Serialization; [YamlSerializable(typeof(GoogleTagManager))] [YamlSerializable(typeof(ContentSource))] [YamlSerializable(typeof(VersionEntry))] +[YamlSerializable(typeof(VersionsConfigDto))] +[YamlSerializable(typeof(VersioningSystemDto))] public partial class YamlStaticContext; diff --git a/src/Elastic.Documentation.Configuration/Versions/Version.cs b/src/Elastic.Documentation.Configuration/Versions/Version.cs new file mode 100644 index 000000000..45aa42893 --- /dev/null +++ b/src/Elastic.Documentation.Configuration/Versions/Version.cs @@ -0,0 +1,98 @@ +// 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.ComponentModel.DataAnnotations; +using NetEscapades.EnumGenerators; +using YamlDotNet.Serialization; + +namespace Elastic.Documentation.Configuration.Versions; + +[YamlSerializable] +public record VersionsConfiguration +{ + public required IReadOnlyDictionary VersioningSystems { get; init; } + public VersioningSystem GetVersioningSystem(VersioningSystemId versioningSystem) + { + if (!VersioningSystems.TryGetValue(versioningSystem, out var version)) + throw new ArgumentException($"Unknown versioning system: {versioningSystem}"); + return version; + } +} + +[EnumExtensions] +public enum VersioningSystemId +{ + [Display(Name = "stack")] + Stack, + [Display(Name = "all")] + All, + [Display(Name = "ece")] + Ece, + [Display(Name = "ech")] + Ech, + [Display(Name = "eck")] + Eck, + [Display(Name = "ess")] + Ess, + [Display(Name = "self")] + Self, + [Display(Name = "ecctl")] + Ecctl, + [Display(Name = "curator")] + Curator, + [Display(Name = "serverless")] + Serverless, + [Display(Name = "elasticsearch")] + Elasticsearch, + [Display(Name = "observability")] + Observability, + [Display(Name = "security")] + Security, + [Display(Name = "apm_agent_android")] + ApmAgentAndroid, + [Display(Name = "apm_agent_ios")] + ApmAgentIos, + [Display(Name = "apm_agent_dotnet")] + ApmAgentDotnet, + [Display(Name = "apm_agent_go")] + ApmAgentGo, + [Display(Name = "apm_agent_java")] + ApmAgentJava, + [Display(Name = "apm_agent_node")] + ApmAgentNode, + [Display(Name = "apm_agent_php")] + ApmAgentPhp, + [Display(Name = "apm_agent_python")] + ApmAgentPython, + [Display(Name = "apm_agent_ruby")] + ApmAgentRuby, + [Display(Name = "apm_agent_rum")] + ApmAgentRum, + [Display(Name = "edot_ios")] + EdotIos, + [Display(Name = "edot_android")] + EdotAndroid, + [Display(Name = "edot_dotnet")] + EdotDotnet, + [Display(Name = "edot_java")] + EdotJava, + [Display(Name = "edot_node")] + EdotNode, + [Display(Name = "edot_php")] + EdotPhp, + [Display(Name = "edot_python")] + EdotPython +} + +[YamlSerializable] +public record VersioningSystem +{ + public required VersioningSystemId Id { get; init; } + + [YamlMember(Alias = "base")] + public required SemVersion Base { get; init; } + + [YamlMember(Alias = "current")] + public required SemVersion Current { get; init; } +} diff --git a/src/Elastic.Documentation.Configuration/Versions/VersionsConfigurationExtensions.cs b/src/Elastic.Documentation.Configuration/Versions/VersionsConfigurationExtensions.cs new file mode 100644 index 000000000..6986fccf4 --- /dev/null +++ b/src/Elastic.Documentation.Configuration/Versions/VersionsConfigurationExtensions.cs @@ -0,0 +1,77 @@ +// 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 Elastic.Documentation.Configuration.Serialization; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; +using YamlDotNet.Serialization; +using YamlDotNet.Serialization.NamingConventions; + +namespace Elastic.Documentation.Configuration.Versions; + +public static class VersionsConfigurationExtensions +{ + private static readonly string ResourceName = "Elastic.Documentation.Configuration.versions.yml"; + + public static IServiceCollection AddVersions(this IServiceCollection services) + { + var assembly = typeof(VersionsConfigurationExtensions).Assembly; + using var stream = assembly.GetManifestResourceStream(ResourceName) ?? throw new FileNotFoundException(ResourceName); + using var reader = new StreamReader(stream); + + var deserializer = new StaticDeserializerBuilder(new YamlStaticContext()) + .WithNamingConvention(UnderscoredNamingConvention.Instance) + .Build(); + + var dto = deserializer.Deserialize(reader); + + var versions = dto.VersioningSystems.ToDictionary( + kvp => ToVersioningSystemId(kvp.Key), + kvp => new VersioningSystem + { + Id = ToVersioningSystemId(kvp.Key), + Base = ToSemVersion(kvp.Value.Base), + Current = ToSemVersion(kvp.Value.Current) + }); + var config = new VersionsConfiguration { VersioningSystems = versions }; + + _ = services.AddSingleton>(new OptionsWrapper(config)); + + return services; + } + + private static VersioningSystemId ToVersioningSystemId(string id) + { + if (!VersioningSystemIdExtensions.TryParse(id, out var versioningSystemId, true, true)) + throw new InvalidOperationException($"Could not parse versioning system id {id}"); + return versioningSystemId; + } + + private static SemVersion ToSemVersion(string semVer) + { + var fullVersion = semVer.Split('.').Length switch + { + 0 => semVer + "0.0.0", + 1 => semVer + ".0.0", + 2 => semVer + ".0", + _ => semVer + }; + if (!SemVersion.TryParse(fullVersion, out var version)) + throw new InvalidOperationException($"Could not parse version {semVer}"); + return version; + } +} + +// Private DTOs for deserialization. These match the YAML structure directly. + +internal sealed record VersionsConfigDto +{ + public Dictionary VersioningSystems { get; set; } = []; +} + +internal sealed record VersioningSystemDto +{ + public string Base { get; set; } = string.Empty; + public string Current { get; set; } = string.Empty; +} diff --git a/src/Elastic.Documentation.Configuration/versions.yml b/src/Elastic.Documentation.Configuration/versions.yml new file mode 100644 index 000000000..ea364d65b --- /dev/null +++ b/src/Elastic.Documentation.Configuration/versions.yml @@ -0,0 +1,87 @@ +versioning_systems: + stack: &stack + base: 9.0 + current: 9.0 + + # Using an unlikely high version + # So that our logic that would display "planned" doesn't trigger + all: &all + base: 99999 + current: 99999 + + # deployment types + ece: + base: 4.0 + current: 4.1 + ech: *all + eck: + base: 3.0 + current: 3.2 + ess: *all + self: *stack + + # things that i don't know about + ecctl: + base: 1.0 + current: 1.15 + curator: + base: 8.0 + current: 8.21 + + # serverless + serverless: *all + elasticsearch: *all + observability: *all + security: *all + + # APM agents + # apm_agent_android: + # base: 1.0 + # current: 1.0.0 + apm_agent_dotnet: + base: 1.0 + current: 1.32.0 + apm_agent_go: + base: 2.0 + current: 2.71 + apm_agent_java: + base: 1.0 + current: 1.54.0 + apm_agent_node: + base: 4.0 + current: 4.13.0 + apm_agent_php: + base: 1.0 + current: 1.15 + apm_agent_python: + base: 6.0 + current: 6.23.0 + apm_agent_ruby: + base: 4.0 + current: 4.7.3 + apm_agent_rum: + base: 5.0 + current: 5.17.0 + + # EDOTs + edot_ios: + base: 1.0 + current: 1.2.1 + edot_android: + base: 1.0 + current: 1.1.0 + edot_dotnet: + base: 1.0 + current: 1.0.2 + edot_java: + base: 1.0 + current: 1.4.1 + edot_node: + base: 1.0 + current: 1.1.1 + edot_php: + base: 1.0 + current: 1.0.0 + edot_python: + base: 1.0 + current: 1.3.0 diff --git a/src/Elastic.Documentation.Site/Assets/main.ts b/src/Elastic.Documentation.Site/Assets/main.ts index de8404f8c..1eb75e5af 100644 --- a/src/Elastic.Documentation.Site/Assets/main.ts +++ b/src/Elastic.Documentation.Site/Assets/main.ts @@ -1,6 +1,7 @@ import { initCopyButton } from './copybutton' import { initDismissibleBanner } from './dismissible-banner' import { initHighlight } from './hljs' +import './markdown/applies-to' import { openDetailsWithAnchor } from './open-details-with-anchor' import { initNav } from './pages-nav' import { initSmoothScroll } from './smooth-scroll' @@ -11,12 +12,13 @@ import 'htmx-ext-head-support' import 'htmx-ext-preload' import 'htmx.org' import { $, $$ } from 'select-dom' -import tippy from 'tippy.js' import { UAParser } from 'ua-parser-js' const { getOS } = new UAParser() -document.addEventListener('htmx:load', function () { +document.addEventListener('htmx:load', function (event) { + console.log('htmx:load') + console.log(event.detail) initTocNav() initHighlight() initCopyButton() @@ -25,9 +27,6 @@ document.addEventListener('htmx:load', function () { initSmoothScroll() openDetailsWithAnchor() initDismissibleBanner() - tippy('[data-tippy-content]:not([data-tippy-content=""])', { - delay: [400, 100], - }) }) // Don't remove style tags because they are used by the elastic global nav. diff --git a/src/Elastic.Documentation.Site/Assets/markdown/applies-to.css b/src/Elastic.Documentation.Site/Assets/markdown/applies-to.css new file mode 100644 index 000000000..eb0e63844 --- /dev/null +++ b/src/Elastic.Documentation.Site/Assets/markdown/applies-to.css @@ -0,0 +1,88 @@ +@layer components { + .applies, + .applies-inline { + cursor: default; + + [data-tippy-content]:not([data-tippy-content='']) { + cursor: help; + border-radius: 9999px; + &:hover { + text-decoration: underline; + } + } + + .applicable-meta-removed { + color: var(--color-red-90); + } + .applicable-meta-ga { + color: var(--color-green-90); + } + .applicable-meta-technical-preview { + color: var(--color-blue-elastic-80); + } + .applicable-meta-deprecated { + color: var(--color-pink-90); + } + .applicable-meta-beta { + color: var(--color-poppy-90); + } + .applicable-meta-planned { + color: var(--color-grey-90); + } + } + + .applies { + @apply font-sans; + border-bottom: 1px solid var(--color-grey-20); + padding-bottom: calc(var(--spacing) * 3); + font-variant: all-petite-caps; + + .applicable-info { + padding: calc(var(--spacing) * 0.5); + padding-left: calc(var(--spacing) * 2); + padding-right: calc(var(--spacing) * 2); + margin: calc(var(--spacing) * 0.5); + display: inline-block; + font-size: 0.8em; + border-radius: 0.4em; + background-color: var(--color-white); + border: 1px solid var(--color-grey-20); + } + .applicable-version { + font-weight: bold; + margin-left: calc(var(--spacing) * 0.5); + font-variant: none; + font-size: 0.87em; + } + .applicable-lifecycle { + font-weight: bold; + } + } + .applies-inline { + @apply font-sans; + font-variant: all-petite-caps; + .applicable-info { + cursor: default; + padding: calc(var(--spacing) * 0.5); + padding-left: calc(var(--spacing) * 2); + padding-right: calc(var(--spacing) * 2); + margin-left: calc(var(--spacing) * 0.5); + margin-right: calc(var(--spacing) * 0.5); + display: inline-block; + font-size: 0.8em; + border-radius: 0.4em; + background-color: var(--color-white); + border: 1px solid var(--color-grey-20); + font-weight: normal; + } + .applicable-version { + font-weight: bold; + margin-left: calc(var(--spacing) * 0.5); + font-variant: none; + font-size: 0.87em; + } + .applicable-lifecycle { + font-weight: bold; + } + } +} diff --git a/src/Elastic.Documentation.Site/Assets/markdown/applies-to.ts b/src/Elastic.Documentation.Site/Assets/markdown/applies-to.ts new file mode 100644 index 000000000..d222c8448 --- /dev/null +++ b/src/Elastic.Documentation.Site/Assets/markdown/applies-to.ts @@ -0,0 +1,14 @@ +import tippy from 'tippy.js' + +document.addEventListener('htmx:load', function () { + tippy( + [ + '.applies [data-tippy-content]:not([data-tippy-content=""])', + '.applies-inline [data-tippy-content]:not([data-tippy-content=""])', + ].join(', '), + { + delay: [400, 100], + hideOnClick: false, + } + ) +}) diff --git a/src/Elastic.Documentation.Site/Assets/styles.css b/src/Elastic.Documentation.Site/Assets/styles.css index 1d3dc3f7d..5f4827038 100644 --- a/src/Elastic.Documentation.Site/Assets/styles.css +++ b/src/Elastic.Documentation.Site/Assets/styles.css @@ -3,6 +3,7 @@ @import './fonts.css'; @import './theme.css'; +@import './markdown/applies-to.css'; @import './markdown/typography.css'; @import './markdown/list.css'; @import './markdown/tabs.css'; @@ -111,85 +112,6 @@ word-break: break-word; } } - - .applies { - @apply font-sans; - border-bottom: 1px solid var(--color-grey-20); - padding-bottom: calc(var(--spacing) * 3); - font-variant: all-petite-caps; - - .applicable-meta-discontinued { - color: var(--color-red-90); - } - .applicable-meta-coming { - color: var(--color-blue-elastic-80); - } - .applicable-meta-technical-preview { - color: var(--color-yellow-80); - } - - .applicable-info { - padding: calc(var(--spacing) * 0.5); - padding-left: calc(var(--spacing) * 2); - padding-right: calc(var(--spacing) * 2); - margin: calc(var(--spacing) * 0.5); - display: inline-block; - font-size: 0.8em; - border-radius: 0.4em; - background-color: var(--color-white); - border: 1px solid var(--color-grey-20); - } - .applicable-version { - font-weight: bold; - margin-left: calc(var(--spacing) * 0.5); - font-variant: none; - font-size: 0.87em; - } - .applicable-lifecycle { - font-weight: bold; - } - } - .applies-inline { - @apply font-sans; - font-variant: all-petite-caps; - - .applicable-meta-discontinued { - color: var(--color-red-90); - } - .applicable-meta-coming { - color: var(--color-blue-elastic-80); - } - .applicable-meta-technical-preview { - color: var(--color-blue-elastic-80); - } - - .applicable-info { - cursor: default; - padding: calc(var(--spacing) * 0.5); - padding-left: calc(var(--spacing) * 2); - padding-right: calc(var(--spacing) * 2); - margin-left: calc(var(--spacing) * 0.5); - margin-right: calc(var(--spacing) * 0.5); - display: inline-block; - font-size: 0.8em; - border-radius: 0.4em; - background-color: var(--color-white); - border: 1px solid var(--color-grey-20); - font-weight: normal; - &[data-tippy-content]:not([data-tippy-content='']) { - cursor: help; - } - } - .applicable-version { - font-weight: bold; - margin-left: calc(var(--spacing) * 0.5); - font-variant: none; - font-size: 0.87em; - } - .applicable-lifecycle { - font-weight: bold; - } - } } * { @@ -299,3 +221,7 @@ body { @apply bg-grey-20 rounded-2xl; } } + +.tippy-content { + white-space: pre-line; +} diff --git a/src/Elastic.Documentation.Site/Assets/theme.css b/src/Elastic.Documentation.Site/Assets/theme.css index 8674a15ec..81fd8af2b 100644 --- a/src/Elastic.Documentation.Site/Assets/theme.css +++ b/src/Elastic.Documentation.Site/Assets/theme.css @@ -157,5 +157,21 @@ --color-grey-140: #1c1f24; --color-grey: #666d78; /* == --color-grey-90 */ + --color-poppy-10: #ffefe9; + --color-poppy-20: #ffe4d9; + --color-poppy-30: #ffd0c0; + --color-poppy-40: #ffc1aa; + --color-poppy-50: #ffad8e; + --color-poppy-60: #ff9873; + --color-poppy-70: #ff8659; + --color-poppy-80: #fa7c56; + --color-poppy-90: #fa744e; + --color-poppy-100: #e86240; + --color-poppy-110: #ce5436; + --color-poppy-120: #b1472e; + --color-poppy-130: #8a3825; + --color-poppy-140: #6b2d1e; + --color-poppy: #fa744e; /* == --color-poppy-90 */ + --spacing: 4px; } diff --git a/src/Elastic.Markdown/HtmlWriter.cs b/src/Elastic.Markdown/HtmlWriter.cs index 43eff502f..21c9fcf83 100644 --- a/src/Elastic.Markdown/HtmlWriter.cs +++ b/src/Elastic.Markdown/HtmlWriter.cs @@ -129,7 +129,8 @@ private async Task RenderLayout(MarkdownFile markdown, MarkdownDocument AllVersionsUrl = allVersionsUrl, LegacyPages = legacyPages?.Skip(1).ToArray(), VersionDropdownItems = VersionDrownDownItemViewModel.FromLegacyPageMappings(legacyPages?.Skip(1).ToArray()), - Products = allProducts + Products = allProducts, + VersionsConfig = DocumentationSet.Context.VersionsConfig }); return await slice.RenderAsync(cancellationToken: ctx); } diff --git a/src/Elastic.Markdown/Myst/CodeBlocks/EnhancedCodeBlockHtmlRenderer.cs b/src/Elastic.Markdown/Myst/CodeBlocks/EnhancedCodeBlockHtmlRenderer.cs index 6646bee40..2903839ee 100644 --- a/src/Elastic.Markdown/Myst/CodeBlocks/EnhancedCodeBlockHtmlRenderer.cs +++ b/src/Elastic.Markdown/Myst/CodeBlocks/EnhancedCodeBlockHtmlRenderer.cs @@ -231,9 +231,18 @@ protected override void Write(HtmlRenderer renderer, EnhancedCodeBlock block) private static void RenderAppliesToHtml(HtmlRenderer renderer, AppliesToDirective appliesToDirective) { var appliesTo = appliesToDirective.AppliesTo; - var slice = AppliesToView.Create(appliesTo); + if (appliesTo is null || appliesTo == FrontMatter.ApplicableTo.All) return; + + var viewModel = new Components.ApplicableToViewModel + { + AppliesTo = appliesTo, + Inline = false, + VersionsConfig = appliesToDirective.Build.VersionsConfig + }; + + var slice = AppliesToView.Create(viewModel); slice.RenderAsync(renderer.Writer).GetAwaiter().GetResult(); } } diff --git a/src/Elastic.Markdown/Myst/Components/ApplicableToComponent.cshtml b/src/Elastic.Markdown/Myst/Components/ApplicableToComponent.cshtml index 96e7bbc61..50f99fb2d 100644 --- a/src/Elastic.Markdown/Myst/Components/ApplicableToComponent.cshtml +++ b/src/Elastic.Markdown/Myst/Components/ApplicableToComponent.cshtml @@ -1,5 +1,6 @@ @using System.Diagnostics.CodeAnalysis @using Elastic.Documentation +@using Elastic.Documentation.Configuration.Versions @using Elastic.Markdown.Myst.FrontMatter @inherits RazorSlice @@ -9,46 +10,90 @@ @if (appliesTo.Stack is not null) { - @RenderProduct("Elastic Stack", appliesTo.Stack) + @RenderProduct( + "Elastic Stack", + "Part of the versioned Elastic Stack\n\nAvailable in ECH, ECE, ECK, and self-managed deployments, unless otherwise stated", + VersioningSystemId.Stack, + appliesTo.Stack + ) } @if (appliesTo.Deployment is not null) { if (appliesTo.Deployment.Ece is not null) { - @RenderProduct("ECE", appliesTo.Deployment.Ece) + @RenderProduct("ECE", + "Elastic Cloud Enterprise", + VersioningSystemId.Ece, + appliesTo.Deployment.Ece + ) } if (appliesTo.Deployment.Eck is not null) { - @RenderProduct("ECK", appliesTo.Deployment.Eck) + @RenderProduct( + "ECK", + "Elastic Cloud on Kubernetes", + VersioningSystemId.Eck, + appliesTo.Deployment.Eck + ) } if (appliesTo.Deployment.Ess is not null) { - @RenderProduct("Elastic Cloud Hosted", appliesTo.Deployment.Ess) + @RenderProduct( + "ECH", + "Elastic Cloud Hosted", + VersioningSystemId.Ess, + appliesTo.Deployment.Ess + ) } if (appliesTo.Deployment.Self is not null) { - @RenderProduct("Self Managed", appliesTo.Deployment.Self) + @RenderProduct( + "Self-Managed", + "Self-managed Elastic deployments", + VersioningSystemId.Self, + appliesTo.Deployment.Self + ) } } @if (appliesTo.Serverless is not null) { if (appliesTo.Serverless.AllProjects is not null) { - @RenderProduct("Serverless", appliesTo.Serverless.AllProjects) + @RenderProduct( + "Serverless", + "Elastic Cloud Serverless: Fully-managed Elastic, available in Elastic Cloud", + VersioningSystemId.Serverless, + appliesTo.Serverless.AllProjects + ) } else { if (appliesTo.Serverless.Elasticsearch is not null) { - @RenderProduct("Serverless Elasticsearch", appliesTo.Serverless.Elasticsearch) + @RenderProduct( + "Serverless Elasticsearch", + "Serverless Elasticsearch projects", + VersioningSystemId.Elasticsearch, + appliesTo.Serverless.Elasticsearch + ) } if (appliesTo.Serverless.Observability is not null) { - @RenderProduct("Serverless Observability", appliesTo.Serverless.Observability) + @RenderProduct( + "Serverless Observability", + "Serverless Observability projects", + VersioningSystemId.Observability, + appliesTo.Serverless.Observability + ) } if (appliesTo.Serverless.Security is not null) { - @RenderProduct("Serverless Security", appliesTo.Serverless.Security) + @RenderProduct( + "Serverless Security", + "Serverless Security projects", + VersioningSystemId.Security, + appliesTo.Serverless.Security + ) } } @@ -56,104 +101,120 @@ } @if (appliesTo.Product is not null) { - @RenderProduct("", appliesTo.Product) + @RenderProduct("", "", VersioningSystemId.All, appliesTo.Product) } @if (appliesTo.ProductApplicability is not null) { var pa = appliesTo.ProductApplicability; - if (pa.Ecctl is not null) { @RenderProduct("ECCTL", pa.Ecctl); } - if (pa.Curator is not null) { @RenderProduct("Curator", pa.Curator); } - if (pa.ApmAgentDotnet is not null) { @RenderProduct("APM Agent .NET", pa.ApmAgentDotnet); } - if (pa.ApmAgentGo is not null) { @RenderProduct("APM Agent Go", pa.ApmAgentGo); } - if (pa.ApmAgentJava is not null) { @RenderProduct("APM Agent Java", pa.ApmAgentJava); } - if (pa.ApmAgentNode is not null) { @RenderProduct("APM Agent Node.js", pa.ApmAgentNode); } - if (pa.ApmAgentPython is not null) { @RenderProduct("APM Agent Python", pa.ApmAgentPython); } - if (pa.ApmAgentRuby is not null) { @RenderProduct("APM Agent Ruby", pa.ApmAgentRuby); } - if (pa.ApmAgentRum is not null) { @RenderProduct("APM Agent RUM", pa.ApmAgentRum); } - if (pa.EdotIos is not null) { @RenderProduct("EDOT iOS", pa.EdotIos); } - if (pa.EdotAndroid is not null) { @RenderProduct("EDOT Android", pa.EdotAndroid); } - if (pa.EdotDotnet is not null) { @RenderProduct("EDOT .NET", pa.EdotDotnet); } - if (pa.EdotJava is not null) { @RenderProduct("EDOT Java", pa.EdotJava); } - if (pa.EdotNode is not null) { @RenderProduct("EDOT Node.js", pa.EdotNode); } - if (pa.EdotPhp is not null) { @RenderProduct("EDOT PHP", pa.EdotPhp); } - if (pa.EdotPython is not null) { @RenderProduct("EDOT Python", pa.EdotPython); } + if (pa.Ecctl is not null) { + @RenderProduct("ECCTL", "Elastic Cloud Control", VersioningSystemId.Ecctl, pa.Ecctl) + ; } + if (pa.Curator is not null) { @RenderProduct("Curator", "", VersioningSystemId.Curator, pa.Curator); } + if (pa.ApmAgentAndroid is not null) { @RenderProduct("APM Agent Android", "", VersioningSystemId.ApmAgentAndroid, pa.ApmAgentAndroid); } + if (pa.ApmAgentDotnet is not null) { @RenderProduct("APM Agent .NET", "", VersioningSystemId.ApmAgentDotnet, pa.ApmAgentDotnet); } + if (pa.ApmAgentGo is not null) { @RenderProduct("APM Agent Go", "", VersioningSystemId.ApmAgentGo, pa.ApmAgentGo); } + if (pa.ApmAgentIos is not null) { @RenderProduct("APM Agent iOS", "", VersioningSystemId.ApmAgentIos, pa.ApmAgentIos); } + if (pa.ApmAgentJava is not null) { @RenderProduct("APM Agent Java", "", VersioningSystemId.ApmAgentJava, pa.ApmAgentJava); } + if (pa.ApmAgentNode is not null) { @RenderProduct("APM Agent Node.js", "", VersioningSystemId.ApmAgentNode, pa.ApmAgentNode); } + if (pa.ApmAgentPhp is not null) { @RenderProduct("APM Agent PHP", "", VersioningSystemId.ApmAgentPhp, pa.ApmAgentPhp); } + if (pa.ApmAgentPython is not null) { @RenderProduct("APM Agent Python", "", VersioningSystemId.ApmAgentPython, pa.ApmAgentPython); } + if (pa.ApmAgentRuby is not null) { @RenderProduct("APM Agent Ruby", "", VersioningSystemId.ApmAgentRuby, pa.ApmAgentRuby); } + if (pa.ApmAgentRum is not null) { @RenderProduct("APM Agent RUM", "", VersioningSystemId.ApmAgentRum, pa.ApmAgentRum); } + if (pa.EdotIos is not null) { @RenderProduct("EDOT iOS", "", VersioningSystemId.EdotIos, pa.EdotIos); } + if (pa.EdotAndroid is not null) { @RenderProduct("EDOT Android", "", VersioningSystemId.EdotAndroid, pa.EdotAndroid); } + if (pa.EdotDotnet is not null) { @RenderProduct("EDOT .NET", "", VersioningSystemId.EdotDotnet, pa.EdotDotnet); } + if (pa.EdotJava is not null) { @RenderProduct("EDOT Java", "", VersioningSystemId.EdotJava, pa.EdotJava); } + if (pa.EdotNode is not null) { @RenderProduct("EDOT Node.js", "", VersioningSystemId.EdotNode, pa.EdotNode); } + if (pa.EdotPhp is not null) { @RenderProduct("EDOT PHP", "", VersioningSystemId.ApmAgentPhp, pa.EdotPhp); } + if (pa.EdotPython is not null) { @RenderProduct("EDOT Python", "", VersioningSystemId.EdotPython, pa.EdotPython); } } @functions { - private IHtmlContent RenderProduct(string name, AppliesCollection applications) + private IHtmlContent RenderProduct(string name, string description, VersioningSystemId versioningSystemName, AppliesCollection applications) { - var currentStackVersion = new SemVersion(9, 0, 0); + var versioningSystem = Model.VersionsConfig.GetVersioningSystem(versioningSystemName); + foreach (var applicability in applications) { - var tooltip = ""; + var lifecycleTooltip = ""; var badgeText = name; - - if (name == "Elastic Stack") + var lifecycleClass = applicability.GetLifeCycleName().ToLowerInvariant().Replace(" ", "-"); + switch (applicability.Lifecycle) { - switch (applicability.Lifecycle) - { - case ProductLifecycle.TechnicalPreview: - if (TryGetRealVersion(applicability, out var previewVersion) && previewVersion > currentStackVersion) - { - badgeText = "Planned"; - tooltip = "We plan to add this functionality in a future update. Plans may change without notice."; - } - else - { - tooltip = "This functionality is in technical preview and may be changed or removed in a future release. Elastic will work to fix any issues, but features in technical preview are not subject to the support SLA of official GA features."; - } - break; - case ProductLifecycle.Beta: - if (TryGetRealVersion(applicability, out var betaVersion) && betaVersion > currentStackVersion) - { - badgeText = "Planned"; - tooltip = "We plan to add this functionality in a future update. Plans may change without notice."; - } - else - { - tooltip = "This functionality is in beta and is subject to change. The design and code is less mature than official GA features and is being provided as-is with no warranties. Beta features are not subject to the support SLA of official GA features."; - } + case ProductLifecycle.TechnicalPreview: + if (TryGetRealVersion(applicability, out var previewVersion) && previewVersion > versioningSystem.Current) + { + badgeText = "Planned"; + lifecycleTooltip = "We plan to add this functionality in a future update. Plans may change without notice."; + lifecycleClass = "planned"; + } + else + { + lifecycleTooltip = "This functionality is in technical preview and may be changed or removed in a future release. Elastic will work to fix any issues, but features in technical preview are not subject to the support SLA of official GA features."; + } + break; + case ProductLifecycle.Beta: + if (TryGetRealVersion(applicability, out var betaVersion) && betaVersion > versioningSystem.Current) + { + badgeText = "Planned"; + lifecycleTooltip = "We plan to add this functionality in a future update. Plans may change without notice."; + lifecycleClass = "planned"; + } + else + { + lifecycleTooltip = "This functionality is in beta and is subject to change. The design and code is less mature than official GA features and is being provided as-is with no warranties. Beta features are not subject to the support SLA of official GA features."; + } - break; - case ProductLifecycle.GenerallyAvailable: - if (TryGetRealVersion(applicability, out var version) && version > currentStackVersion) - { - badgeText = "Planned"; - tooltip = "We plan to add this functionality in a future update. Plans may change without notice."; - } + break; + case ProductLifecycle.GenerallyAvailable: + if (TryGetRealVersion(applicability, out var version) && version > versioningSystem.Current) + { + badgeText = "Planned"; + lifecycleTooltip = "We plan to add this functionality in a future update. Plans may change without notice."; + lifecycleClass = "planned"; + } - break; - case ProductLifecycle.Deprecated: - if (TryGetRealVersion(applicability, out var deprecatedVersion) && deprecatedVersion > currentStackVersion) - { - badgeText = "Deprecation planned"; - tooltip = "We plan to deprecate this functionality in a future update. Plans may change without notice."; - } + break; + case ProductLifecycle.Deprecated: + if (TryGetRealVersion(applicability, out var deprecatedVersion) && deprecatedVersion > versioningSystem.Current) + { + badgeText = "Deprecation planned"; + lifecycleTooltip = "We plan to deprecate this functionality in a future update. Plans may change without notice."; + } - break; - case ProductLifecycle.Removed: - if (TryGetRealVersion(applicability, out var removedVersion) && removedVersion > currentStackVersion) - { - badgeText = "Removal planned"; - tooltip = "We plan to remove this functionality in a future update. Plans may change without notice."; - } - break; - } + break; + case ProductLifecycle.Removed: + if (TryGetRealVersion(applicability, out var removedVersion) && removedVersion > versioningSystem.Current) + { + badgeText = "Removal planned"; + lifecycleTooltip = "We plan to remove this functionality in a future update. Plans may change without notice."; + } + break; + } + + if ( + applicability.Version is null + && applicability.Lifecycle != ProductLifecycle.Deprecated + && applicability.Lifecycle != ProductLifecycle.Removed + && versioningSystem.Id is VersioningSystemId.Stack or VersioningSystemId.Self or VersioningSystemId.Ece or VersioningSystemId.Eck + ) + { + lifecycleTooltip += $"\n\nApplies to version {versioningSystem.Base} and later - may have been introduced in a previous version"; } - var lifecycleClass = applicability.GetLifeCycleName().ToLowerInvariant().Replace(" ", "-"); - - @name - - @if (applicability.Lifecycle != ProductLifecycle.GenerallyAvailable && badgeText == name) + var badgeTextChanged = badgeText != name; + + @name + + @if (applicability.Lifecycle != ProductLifecycle.GenerallyAvailable && !badgeTextChanged) { @applicability.GetLifeCycleName() } @if (applicability.Version is not null and not AllVersions) { - @if (name != "Elastic Stack" || applicability.Version <= currentStackVersion) + @if (versioningSystem.Current >= applicability.Version) { @applicability.Version diff --git a/src/Elastic.Markdown/Myst/Components/ApplicableToViewModel.cs b/src/Elastic.Markdown/Myst/Components/ApplicableToViewModel.cs index b535c2a24..bf71bb532 100644 --- a/src/Elastic.Markdown/Myst/Components/ApplicableToViewModel.cs +++ b/src/Elastic.Markdown/Myst/Components/ApplicableToViewModel.cs @@ -2,6 +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 Elastic.Documentation.Configuration.Versions; using Elastic.Markdown.Myst.FrontMatter; namespace Elastic.Markdown.Myst.Components; @@ -10,4 +11,5 @@ public class ApplicableToViewModel { public required bool Inline { get; init; } public required ApplicableTo AppliesTo { get; init; } + public required VersionsConfiguration VersionsConfig { get; init; } } diff --git a/src/Elastic.Markdown/Myst/Directives/AppliesTo/AppliesToView.cshtml b/src/Elastic.Markdown/Myst/Directives/AppliesTo/AppliesToView.cshtml index fb8183845..7610f59e2 100644 --- a/src/Elastic.Markdown/Myst/Directives/AppliesTo/AppliesToView.cshtml +++ b/src/Elastic.Markdown/Myst/Directives/AppliesTo/AppliesToView.cshtml @@ -1,11 +1,6 @@ @using Elastic.Markdown.Myst.Components -@using Elastic.Markdown.Myst.FrontMatter -@inherits RazorSlice +@inherits RazorSlice

-@await RenderPartialAsync(ApplicableToComponent.Create(new ApplicableToViewModel -{ - AppliesTo = Model, - Inline = false -})) -

\ No newline at end of file +@await RenderPartialAsync(ApplicableToComponent.Create(Model)) +

diff --git a/src/Elastic.Markdown/Myst/FrontMatter/Applicability.cs b/src/Elastic.Markdown/Myst/FrontMatter/Applicability.cs index 16f5457c4..cefd1f04e 100644 --- a/src/Elastic.Markdown/Myst/FrontMatter/Applicability.cs +++ b/src/Elastic.Markdown/Myst/FrontMatter/Applicability.cs @@ -178,9 +178,9 @@ public static bool TryParse(string? value, IList<(Severity, string)> diagnostics "ga" => ProductLifecycle.GenerallyAvailable, "deprecated" => ProductLifecycle.Deprecated, "removed" => ProductLifecycle.Removed, - "unavailable" => ProductLifecycle.Unavailable, // OBSOLETE should be removed once docs are cleaned up + "unavailable" => ProductLifecycle.Unavailable, "dev" => ProductLifecycle.Development, "development" => ProductLifecycle.Development, "coming" => ProductLifecycle.Planned, @@ -188,9 +188,15 @@ public static bool TryParse(string? value, IList<(Severity, string)> diagnostics "discontinued" => ProductLifecycle.Discontinued, _ => throw new Exception($"Unknown product lifecycle: {tokens[0]}") }; + var deprecatedLifecycles = new[] + { + ProductLifecycle.Development, + ProductLifecycle.Planned, + ProductLifecycle.Discontinued + }; // TODO emit as error when all docs have been updated - if (lifecycle is ProductLifecycle.Planned or ProductLifecycle.Development) + if (deprecatedLifecycles.Contains(lifecycle)) diagnostics.Add((Severity.Hint, $"The '{lookup}' lifecycle is deprecated and will be removed in a future release.")); var version = tokens.Length < 2 diff --git a/src/Elastic.Markdown/Myst/FrontMatter/ApplicableTo.cs b/src/Elastic.Markdown/Myst/FrontMatter/ApplicableTo.cs index d6267d4b1..9568cb44e 100644 --- a/src/Elastic.Markdown/Myst/FrontMatter/ApplicableTo.cs +++ b/src/Elastic.Markdown/Myst/FrontMatter/ApplicableTo.cs @@ -124,18 +124,27 @@ public record ProductApplicability [YamlMember(Alias = "curator")] public AppliesCollection? Curator { get; set; } + [YamlMember(Alias = "apm_agent_android")] + public AppliesCollection? ApmAgentAndroid { get; set; } + [YamlMember(Alias = "apm_agent_dotnet")] public AppliesCollection? ApmAgentDotnet { get; set; } [YamlMember(Alias = "apm_agent_go")] public AppliesCollection? ApmAgentGo { get; set; } + [YamlMember(Alias = "apm_agent_ios")] + public AppliesCollection? ApmAgentIos { get; set; } + [YamlMember(Alias = "apm_agent_java")] public AppliesCollection? ApmAgentJava { get; set; } [YamlMember(Alias = "apm_agent_node")] public AppliesCollection? ApmAgentNode { get; set; } + [YamlMember(Alias = "apm_agent_php")] + public AppliesCollection? ApmAgentPhp { get; set; } + [YamlMember(Alias = "apm_agent_python")] public AppliesCollection? ApmAgentPython { get; set; } @@ -175,7 +184,7 @@ public class ApplicableToConverter : IYamlTypeConverter "ece", "eck", "ess", "self", "elasticsearch", "observability", "security", "ecctl", "curator", - "apm_agent_dotnet", "apm_agent_go", "apm_agent_java", "apm_agent_node", "apm_agent_python", "apm_agent_ruby", "apm_agent_rum", + "apm_agent_android","apm_agent_dotnet", "apm_agent_go", "apm_agent_ios", "apm_agent_java", "apm_agent_node", "apm_agent_php", "apm_agent_python", "apm_agent_ruby", "apm_agent_rum", "edot_ios", "edot_android", "edot_dotnet", "edot_java", "edot_node", "edot_php", "edot_python" ]; @@ -304,37 +313,27 @@ private static bool TryGetDeployment(Dictionary dictionary, Lis applicability = null; var d = new DeploymentApplicability(); var assigned = false; - if (TryGetApplicabilityOverTime(dictionary, "ece", diagnostics, out var ece)) - { - d.Ece = ece; - assigned = true; - } - if (TryGetApplicabilityOverTime(dictionary, "eck", diagnostics, out var eck)) + var mapping = new Dictionary> { - d.Eck = eck; - assigned = true; - } + { "ece", a => d.Ece = a }, + { "eck", a => d.Eck = a }, + { "ess", a => d.Ess = a }, + { "self", a => d.Self = a } + }; - if (TryGetApplicabilityOverTime(dictionary, "ess", diagnostics, out var ess)) + foreach (var (key, action) in mapping) { - d.Ess = ess; + if (!TryGetApplicabilityOverTime(dictionary, key, diagnostics, out var collection)) + continue; + action(collection); assigned = true; } - if (TryGetApplicabilityOverTime(dictionary, "self", diagnostics, out var self)) - { - d.Self = self; - assigned = true; - } - - if (assigned) - { - applicability = d; - return true; - } - - return false; + if (!assigned) + return false; + applicability = d; + return true; } private static bool TryGetProjectApplicability(Dictionary dictionary, @@ -344,21 +343,19 @@ private static bool TryGetProjectApplicability(Dictionary dicti applicability = null; var serverlessAvailability = new ServerlessProjectApplicability(); var assigned = false; - if (TryGetApplicabilityOverTime(dictionary, "elasticsearch", diagnostics, out var elasticsearch)) - { - serverlessAvailability.Elasticsearch = elasticsearch; - assigned = true; - } - if (TryGetApplicabilityOverTime(dictionary, "observability", diagnostics, out var observability)) + var mapping = new Dictionary> { - serverlessAvailability.Observability = observability; - assigned = true; - } + ["elasticsearch"] = a => serverlessAvailability.Elasticsearch = a, + ["observability"] = a => serverlessAvailability.Observability = a, + ["security"] = a => serverlessAvailability.Security = a + }; - if (TryGetApplicabilityOverTime(dictionary, "security", diagnostics, out var security)) + foreach (var (key, action) in mapping) { - serverlessAvailability.Security = security; + if (!TryGetApplicabilityOverTime(dictionary, key, diagnostics, out var collection)) + continue; + action(collection); assigned = true; } @@ -375,99 +372,35 @@ private static bool TryGetProductApplicability(Dictionary dicti applicability = null; var productAvailability = new ProductApplicability(); var assigned = false; - if (TryGetApplicabilityOverTime(dictionary, "ecctl", diagnostics, out var ecctl)) - { - productAvailability.Ecctl = ecctl; - assigned = true; - } - - if (TryGetApplicabilityOverTime(dictionary, "curator", diagnostics, out var curator)) - { - productAvailability.Curator = curator; - assigned = true; - } - - if (TryGetApplicabilityOverTime(dictionary, "apm_agent_dotnet", diagnostics, out var apmAgentDotnet)) - { - productAvailability.ApmAgentDotnet = apmAgentDotnet; - assigned = true; - } - - if (TryGetApplicabilityOverTime(dictionary, "apm_agent_go", diagnostics, out var apmAgentGo)) - { - productAvailability.ApmAgentGo = apmAgentGo; - assigned = true; - } - if (TryGetApplicabilityOverTime(dictionary, "apm_agent_java", diagnostics, out var apmAgentJava)) + var mapping = new Dictionary> { - productAvailability.ApmAgentJava = apmAgentJava; - assigned = true; - } - - if (TryGetApplicabilityOverTime(dictionary, "apm_agent_node", diagnostics, out var apmAgentNode)) - { - productAvailability.ApmAgentNode = apmAgentNode; - assigned = true; - } - - if (TryGetApplicabilityOverTime(dictionary, "apm_agent_python", diagnostics, out var apmAgentPython)) - { - productAvailability.ApmAgentPython = apmAgentPython; - assigned = true; - } - - if (TryGetApplicabilityOverTime(dictionary, "apm_agent_ruby", diagnostics, out var apmAgentRuby)) - { - productAvailability.ApmAgentRuby = apmAgentRuby; - assigned = true; - } - - if (TryGetApplicabilityOverTime(dictionary, "apm_agent_rum", diagnostics, out var apmAgentRum)) - { - productAvailability.ApmAgentRum = apmAgentRum; - assigned = true; - } - - if (TryGetApplicabilityOverTime(dictionary, "edot_ios", diagnostics, out var edotIos)) - { - productAvailability.EdotIos = edotIos; - assigned = true; - } - - if (TryGetApplicabilityOverTime(dictionary, "edot_android", diagnostics, out var edotAndroid)) - { - productAvailability.EdotAndroid = edotAndroid; - assigned = true; - } - - if (TryGetApplicabilityOverTime(dictionary, "edot_dotnet", diagnostics, out var edotDotnet)) - { - productAvailability.EdotDotnet = edotDotnet; - assigned = true; - } - - if (TryGetApplicabilityOverTime(dictionary, "edot_java", diagnostics, out var edotJava)) - { - productAvailability.EdotJava = edotJava; - assigned = true; - } - - if (TryGetApplicabilityOverTime(dictionary, "edot_node", diagnostics, out var edotNode)) - { - productAvailability.EdotNode = edotNode; - assigned = true; - } - - if (TryGetApplicabilityOverTime(dictionary, "edot_php", diagnostics, out var edotPhp)) - { - productAvailability.EdotPhp = edotPhp; - assigned = true; - } - - if (TryGetApplicabilityOverTime(dictionary, "edot_python", diagnostics, out var edotPython)) + { "ecctl", a => productAvailability.Ecctl = a }, + { "curator", a => productAvailability.Curator = a }, + { "apm_agent_android", a => productAvailability.ApmAgentAndroid = a }, + { "apm_agent_dotnet", a => productAvailability.ApmAgentDotnet = a }, + { "apm_agent_go", a => productAvailability.ApmAgentGo = a }, + { "apm_agent_ios", a => productAvailability.ApmAgentIos = a }, + { "apm_agent_java", a => productAvailability.ApmAgentJava = a }, + { "apm_agent_node", a => productAvailability.ApmAgentNode = a }, + { "apm_agent_php", a => productAvailability.ApmAgentPhp = a }, + { "apm_agent_python", a => productAvailability.ApmAgentPython = a }, + { "apm_agent_ruby", a => productAvailability.ApmAgentRuby = a }, + { "apm_agent_rum", a => productAvailability.ApmAgentRum = a }, + { "edot_ios", a => productAvailability.EdotIos = a }, + { "edot_android", a => productAvailability.EdotAndroid = a }, + { "edot_dotnet", a => productAvailability.EdotDotnet = a }, + { "edot_java", a => productAvailability.EdotJava = a }, + { "edot_node", a => productAvailability.EdotNode = a }, + { "edot_php", a => productAvailability.EdotPhp = a }, + { "edot_python", a => productAvailability.EdotPython = a } + }; + + foreach (var (key, action) in mapping) { - productAvailability.EdotPython = edotPython; + if (!TryGetApplicabilityOverTime(dictionary, key, diagnostics, out var collection)) + continue; + action(collection); assigned = true; } diff --git a/src/Elastic.Markdown/Myst/Roles/AppliesTo/ApplicableToRole.cshtml b/src/Elastic.Markdown/Myst/Roles/AppliesTo/ApplicableToRole.cshtml index dd204468d..df7f628c6 100644 --- a/src/Elastic.Markdown/Myst/Roles/AppliesTo/ApplicableToRole.cshtml +++ b/src/Elastic.Markdown/Myst/Roles/AppliesTo/ApplicableToRole.cshtml @@ -1,11 +1,6 @@ @using Elastic.Markdown.Myst.Components -@using Elastic.Markdown.Myst.FrontMatter -@inherits RazorSlice +@inherits RazorSlice -@await RenderPartialAsync(ApplicableToComponent.Create(new ApplicableToViewModel -{ - AppliesTo = Model, - Inline = true -})) - \ No newline at end of file +@await RenderPartialAsync(ApplicableToComponent.Create(Model)) +
diff --git a/src/Elastic.Markdown/Myst/Roles/AppliesTo/AppliesToRole.cs b/src/Elastic.Markdown/Myst/Roles/AppliesTo/AppliesToRole.cs index 0ea809c94..f561c281c 100644 --- a/src/Elastic.Markdown/Myst/Roles/AppliesTo/AppliesToRole.cs +++ b/src/Elastic.Markdown/Myst/Roles/AppliesTo/AppliesToRole.cs @@ -4,6 +4,7 @@ using System.Diagnostics; using Elastic.Documentation; +using Elastic.Documentation.Configuration; using Elastic.Markdown.Diagnostics; using Elastic.Markdown.Myst.FrontMatter; using Markdig; @@ -17,11 +18,16 @@ namespace Elastic.Markdown.Myst.Roles.AppliesTo; [DebuggerDisplay("{GetType().Name} Line: {Line}, Role: {Role}, Content: {Content}")] public class AppliesToRole : RoleLeaf, IApplicableToElement { - public AppliesToRole(string role, string content, InlineProcessor parserContext) : base(role, content) => + public AppliesToRole(string role, string content, InlineProcessor parserContext) : base(role, content) + { AppliesTo = ParseApplicableTo(content, parserContext); + BuildContext = parserContext.GetContext().Build; + } public ApplicableTo? AppliesTo { get; } + public BuildContext BuildContext { get; } + private ApplicableTo? ParseApplicableTo(string yaml, InlineProcessor processor) { try @@ -85,6 +91,3 @@ public void Setup(MarkdownPipelineBuilder pipeline) public void Setup(MarkdownPipeline pipeline, IMarkdownRenderer renderer) => renderer.ObjectRenderers.InsertBefore(new AppliesToRoleHtmlRenderer()); } - - - diff --git a/src/Elastic.Markdown/Myst/Roles/AppliesTo/AppliesToRoleRenderer.cs b/src/Elastic.Markdown/Myst/Roles/AppliesTo/AppliesToRoleRenderer.cs index 02374fd2d..566814384 100644 --- a/src/Elastic.Markdown/Myst/Roles/AppliesTo/AppliesToRoleRenderer.cs +++ b/src/Elastic.Markdown/Myst/Roles/AppliesTo/AppliesToRoleRenderer.cs @@ -15,9 +15,20 @@ public class AppliesToRoleHtmlRenderer : HtmlObjectRenderer protected override void Write(HtmlRenderer renderer, AppliesToRole role) { var appliesTo = role.AppliesTo; - var slice = ApplicableToRole.Create(appliesTo); + + // Skip rendering if appliesTo is null or All if (appliesTo is null || appliesTo == FrontMatter.ApplicableTo.All) return; + + // Create the view model with the VersionsConfig from the role's BuildContext + var viewModel = new Components.ApplicableToViewModel + { + AppliesTo = appliesTo, + Inline = true, + VersionsConfig = role.BuildContext.VersionsConfig + }; + + var slice = ApplicableToRole.Create(viewModel); var html = slice.RenderAsync().GetAwaiter().GetResult(); _ = renderer.Write(html); } diff --git a/src/Elastic.Markdown/Myst/Roles/RoleParser.cs b/src/Elastic.Markdown/Myst/Roles/RoleParser.cs index 1325bb55d..6b76e9fed 100644 --- a/src/Elastic.Markdown/Myst/Roles/RoleParser.cs +++ b/src/Elastic.Markdown/Myst/Roles/RoleParser.cs @@ -31,6 +31,7 @@ public abstract class RoleParser : InlineParser public override bool Match(InlineProcessor processor, ref StringSlice slice) { + var match = slice.CurrentChar; if (processor.Context is not ParserContext) @@ -88,7 +89,6 @@ public override bool Match(InlineProcessor processor, ref StringSlice slice) var endIndex = Math.Min(startContent + i + 2, span.Length); var contentSpan = span[startContent..endIndex]; - // var contentSpan = span[startContent..(startContent + i + 2)]; var startPosition = slice.Start; slice.Start = startPosition + roleContent.Length + contentSpan.Length; diff --git a/src/Elastic.Markdown/Page/Index.cshtml b/src/Elastic.Markdown/Page/Index.cshtml index e10ab5379..2338c753e 100644 --- a/src/Elastic.Markdown/Page/Index.cshtml +++ b/src/Elastic.Markdown/Page/Index.cshtml @@ -65,7 +65,8 @@ @await RenderPartialAsync(ApplicableToComponent.Create(new ApplicableToViewModel { AppliesTo = Model.AppliesTo, - Inline = false + Inline = false, + VersionsConfig = Model.VersionsConfig }))

} diff --git a/src/Elastic.Markdown/Page/IndexViewModel.cs b/src/Elastic.Markdown/Page/IndexViewModel.cs index d92559614..b12f097bb 100644 --- a/src/Elastic.Markdown/Page/IndexViewModel.cs +++ b/src/Elastic.Markdown/Page/IndexViewModel.cs @@ -5,6 +5,7 @@ using System.Text.Json.Serialization; using Elastic.Documentation.Configuration.Assembler; using Elastic.Documentation.Configuration.Builder; +using Elastic.Documentation.Configuration.Versions; using Elastic.Documentation.Legacy; using Elastic.Documentation.Site.FileProviders; using Elastic.Documentation.Site.Navigation; @@ -50,6 +51,8 @@ public class IndexViewModel public required StaticFileContentHashProvider StaticFileContentHashProvider { get; init; } public required HashSet Products { get; init; } + + public required VersionsConfiguration VersionsConfig { get; init; } } public class VersionDrownDownItemViewModel diff --git a/src/tooling/Elastic.Documentation.Tooling/DocumentationTooling.cs b/src/tooling/Elastic.Documentation.Tooling/DocumentationTooling.cs index 8d97657ef..4220d0935 100644 --- a/src/tooling/Elastic.Documentation.Tooling/DocumentationTooling.cs +++ b/src/tooling/Elastic.Documentation.Tooling/DocumentationTooling.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information using Actions.Core.Extensions; +using Elastic.Documentation.Configuration.Versions; using Elastic.Documentation.Tooling.Logging; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection.Extensions; @@ -27,7 +28,8 @@ public static ServiceProvider CreateServiceProvider(ref string[] args, Action()); _ = services.AddLogging(x => x .ClearProviders() diff --git a/src/tooling/docs-assembler/Cli/RepositoryCommands.cs b/src/tooling/docs-assembler/Cli/RepositoryCommands.cs index 6e0e645bf..caa6e6c8d 100644 --- a/src/tooling/docs-assembler/Cli/RepositoryCommands.cs +++ b/src/tooling/docs-assembler/Cli/RepositoryCommands.cs @@ -13,8 +13,10 @@ using Documentation.Assembler.Legacy; using Documentation.Assembler.Navigation; using Documentation.Assembler.Sourcing; +using Elastic.Documentation; using Elastic.Documentation.Configuration; using Elastic.Documentation.Configuration.Assembler; +using Elastic.Documentation.Configuration.Versions; using Elastic.Documentation.LegacyDocs; using Elastic.Documentation.Tooling.Diagnostics.Console; using Elastic.Markdown; @@ -164,6 +166,20 @@ public async Task UpdateLinkIndexAll(ContentSource contentSource, Cancel ct { { NarrativeRepository.RepositoryName, assembleContext.Configuration.Narrative } }; + var versionsConfig = new VersionsConfiguration + { + VersioningSystems = new Dictionary + { + { + VersioningSystemId.Stack, new VersioningSystem + { + Id = VersioningSystemId.Stack, + Current = new SemVersion(8, 0, 0), + Base = new SemVersion(8, 0, 0) + } + } + } + }; await Parallel.ForEachAsync(repositories, new ParallelOptions { @@ -179,6 +195,7 @@ await Parallel.ForEachAsync(repositories, collector, new FileSystem(), new FileSystem(), + versionsConfig, checkout.Directory.FullName, outputPath ); diff --git a/src/tooling/docs-assembler/Navigation/AssemblerDocumentationSet.cs b/src/tooling/docs-assembler/Navigation/AssemblerDocumentationSet.cs index d6f13925b..9ae2885d8 100644 --- a/src/tooling/docs-assembler/Navigation/AssemblerDocumentationSet.cs +++ b/src/tooling/docs-assembler/Navigation/AssemblerDocumentationSet.cs @@ -6,6 +6,7 @@ using Elastic.Documentation; using Elastic.Documentation.Configuration; using Elastic.Documentation.Configuration.Assembler; +using Elastic.Documentation.Configuration.Versions; using Elastic.Markdown.IO; using Elastic.Markdown.IO.Navigation; using Elastic.Markdown.Links.CrossLinks; @@ -48,10 +49,26 @@ public AssemblerDocumentationSet( Branch = checkout.Repository.GitReferenceCurrent }; + var versionsConfig = new VersionsConfiguration + { + VersioningSystems = new Dictionary + { + { + VersioningSystemId.Stack, new VersioningSystem + { + Id = VersioningSystemId.Stack, + Current = new SemVersion(8, 0, 0), + Base = new SemVersion(8, 0, 0) + } + } + } + }; + var buildContext = new BuildContext( context.Collector, context.ReadFileSystem, context.WriteFileSystem, + versionsConfig, path, output, gitConfiguration diff --git a/src/tooling/docs-builder/Cli/Commands.cs b/src/tooling/docs-builder/Cli/Commands.cs index c509d79da..178ebb22a 100644 --- a/src/tooling/docs-builder/Cli/Commands.cs +++ b/src/tooling/docs-builder/Cli/Commands.cs @@ -10,6 +10,7 @@ using Documentation.Builder.Http; using Elastic.ApiExplorer; using Elastic.Documentation.Configuration; +using Elastic.Documentation.Configuration.Versions; using Elastic.Documentation.Refactor; using Elastic.Documentation.Tooling.Diagnostics.Console; using Elastic.Documentation.Tooling.Filters; @@ -17,12 +18,14 @@ using Elastic.Markdown.Exporters; using Elastic.Markdown.IO; using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; namespace Documentation.Builder.Cli; -internal sealed class Commands(ILoggerFactory logger, ICoreService githubActionsService) +internal sealed class Commands(ILoggerFactory logger, ICoreService githubActionsService, IOptions versionsConfigOption) { private readonly ILogger _log = logger.CreateLogger(); + [SuppressMessage("Usage", "CA2254:Template should be a static expression")] private void AssignOutputLogger() { @@ -44,7 +47,7 @@ private void AssignOutputLogger() public async Task Serve(string? path = null, int port = 3000, Cancel ctx = default) { AssignOutputLogger(); - var host = new DocumentationWebHost(path, port, logger, new FileSystem(), new MockFileSystem()); + var host = new DocumentationWebHost(path, port, logger, new FileSystem(), new MockFileSystem(), versionsConfigOption.Value); _log.LogInformation("Find your documentation at http://localhost:{Port}/{Path}", port, host.GeneratorState.Generator.DocumentationSet.FirstInterestingUrl.TrimStart('/') ); @@ -120,7 +123,7 @@ public async Task Generate( try { - context = new BuildContext(collector, fileSystem, fileSystem, path, output) + context = new BuildContext(collector, fileSystem, fileSystem, versionsConfigOption.Value, path, output) { UrlPathPrefix = pathPrefix, Force = force ?? false, @@ -229,7 +232,7 @@ public async Task Move( AssignOutputLogger(); var fileSystem = new FileSystem(); await using var collector = new ConsoleDiagnosticsCollector(logger, null).StartAsync(ctx); - var context = new BuildContext(collector, fileSystem, fileSystem, path, null); + var context = new BuildContext(collector, fileSystem, fileSystem, versionsConfigOption.Value, path, null); var set = new DocumentationSet(context, logger); var moveCommand = new Move(fileSystem, fileSystem, set, logger); diff --git a/src/tooling/docs-builder/Cli/DiffCommands.cs b/src/tooling/docs-builder/Cli/DiffCommands.cs index 8e5691aef..618f4fb32 100644 --- a/src/tooling/docs-builder/Cli/DiffCommands.cs +++ b/src/tooling/docs-builder/Cli/DiffCommands.cs @@ -9,13 +9,15 @@ using Documentation.Builder.Tracking; using Elastic.Documentation.Configuration; using Elastic.Documentation.Configuration.Builder; +using Elastic.Documentation.Configuration.Versions; using Elastic.Documentation.Tooling.Diagnostics.Console; using Elastic.Documentation.Tooling.Filters; using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; namespace Documentation.Builder.Cli; -internal sealed class DiffCommands(ILoggerFactory logger, ICoreService githubActionsService) +internal sealed class DiffCommands(ILoggerFactory logger, ICoreService githubActionsService, IOptions versionsConfigOption) { /// /// Validates redirect updates in the current branch using the redirects file against changes reported by git. @@ -39,7 +41,7 @@ public async Task ValidateRedirects([Argument] string? path = null, Cancel var fs = new FileSystem(); var root = fs.DirectoryInfo.New(Paths.WorkingDirectoryRoot.FullName); - var buildContext = new BuildContext(collector, fs, fs, root.FullName, null); + var buildContext = new BuildContext(collector, fs, fs, versionsConfigOption.Value, root.FullName, null); var sourceFile = buildContext.ConfigurationPath; var redirectFileName = sourceFile.Name.StartsWith('_') ? "_redirects.yml" : "redirects.yml"; var redirectFileInfo = sourceFile.FileSystem.FileInfo.New(Path.Combine(sourceFile.Directory!.FullName, redirectFileName)); diff --git a/src/tooling/docs-builder/Http/DocumentationWebHost.cs b/src/tooling/docs-builder/Http/DocumentationWebHost.cs index 60f53b9f0..ad21644f5 100644 --- a/src/tooling/docs-builder/Http/DocumentationWebHost.cs +++ b/src/tooling/docs-builder/Http/DocumentationWebHost.cs @@ -7,6 +7,7 @@ using System.Runtime.InteropServices; using Documentation.Builder.Diagnostics.LiveMode; using Elastic.Documentation.Configuration; +using Elastic.Documentation.Configuration.Versions; using Elastic.Documentation.Site.FileProviders; using Elastic.Documentation.Tooling; using Elastic.Markdown.IO; @@ -27,7 +28,7 @@ public class DocumentationWebHost private readonly IHostedService _hostedService; private readonly IFileSystem _writeFileSystem; - public DocumentationWebHost(string? path, int port, ILoggerFactory logger, IFileSystem readFs, IFileSystem writeFs) + public DocumentationWebHost(string? path, int port, ILoggerFactory logger, IFileSystem readFs, IFileSystem writeFs, VersionsConfiguration versionsConfig) { _writeFileSystem = writeFs; var builder = WebApplication.CreateSlimBuilder(); @@ -43,7 +44,7 @@ public DocumentationWebHost(string? path, int port, ILoggerFactory logger, IFile var hostUrl = $"http://localhost:{port}"; _hostedService = collector; - Context = new BuildContext(collector, readFs, writeFs, path, null) + Context = new BuildContext(collector, readFs, writeFs, versionsConfig, path, null) { CanonicalBaseUrl = new Uri(hostUrl), }; diff --git a/tests/Elastic.ApiExplorer.Tests/ReaderTests.cs b/tests/Elastic.ApiExplorer.Tests/ReaderTests.cs index 07ade9f28..f9c734b12 100644 --- a/tests/Elastic.ApiExplorer.Tests/ReaderTests.cs +++ b/tests/Elastic.ApiExplorer.Tests/ReaderTests.cs @@ -5,6 +5,7 @@ using System.IO.Abstractions; using Elastic.Documentation; using Elastic.Documentation.Configuration; +using Elastic.Documentation.Configuration.Versions; using Elastic.Documentation.Diagnostics; using FluentAssertions; using Microsoft.Extensions.Logging.Abstractions; @@ -18,7 +19,21 @@ public class ReaderTests public async Task Reads() { var collector = new DiagnosticsCollector([]); - var context = new BuildContext(collector, new FileSystem()); + var versionsConfig = new VersionsConfiguration + { + VersioningSystems = new Dictionary + { + { + VersioningSystemId.Stack, new VersioningSystem + { + Id = VersioningSystemId.Stack, + Current = new SemVersion(8, 0, 0), + Base = new SemVersion(8, 0, 0) + } + } + } + }; + var context = new BuildContext(collector, new FileSystem(), versionsConfig); context.Configuration.OpenApiSpecification.Should().NotBeNull(); @@ -31,8 +46,22 @@ public async Task Reads() [Fact] public async Task Navigation() { + var versionsConfig = new VersionsConfiguration + { + VersioningSystems = new Dictionary + { + { + VersioningSystemId.Stack, new VersioningSystem + { + Id = VersioningSystemId.Stack, + Current = new SemVersion(8, 0, 0), + Base = new SemVersion(8, 0, 0) + } + } + } + }; var collector = new DiagnosticsCollector([]); - var context = new BuildContext(collector, new FileSystem()); + var context = new BuildContext(collector, new FileSystem(), versionsConfig); var generator = new OpenApiGenerator(context, NoopMarkdownStringRenderer.Instance, NullLoggerFactory.Instance); context.Configuration.OpenApiSpecification.Should().NotBeNull(); diff --git a/tests/Elastic.Markdown.Tests/Directives/DirectiveBaseTests.cs b/tests/Elastic.Markdown.Tests/Directives/DirectiveBaseTests.cs index 8008367a8..c48d0b673 100644 --- a/tests/Elastic.Markdown.Tests/Directives/DirectiveBaseTests.cs +++ b/tests/Elastic.Markdown.Tests/Directives/DirectiveBaseTests.cs @@ -2,7 +2,9 @@ // 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 Elastic.Documentation; using Elastic.Documentation.Configuration; +using Elastic.Documentation.Configuration.Versions; using Elastic.Markdown.IO; using Elastic.Markdown.Myst.Directives; using FluentAssertions; @@ -68,7 +70,21 @@ protected DirectiveTest(ITestOutputHelper output, [LanguageInjection("markdown") FileSystem.GenerateDocSetYaml(root); Collector = new TestDiagnosticsCollector(output); - var context = new BuildContext(Collector, FileSystem); + var versionsConfig = new VersionsConfiguration + { + VersioningSystems = new Dictionary + { + { + VersioningSystemId.Stack, new VersioningSystem + { + Id = VersioningSystemId.Stack, + Current = new SemVersion(8, 0, 0), + Base = new SemVersion(8, 0, 0) + } + } + } + }; + var context = new BuildContext(Collector, FileSystem, versionsConfig); var linkResolver = new TestCrossLinkResolver(); Set = new DocumentationSet(context, logger, linkResolver); File = Set.DocumentationFileLookup(FileSystem.FileInfo.New("docs/index.md")) as MarkdownFile ?? throw new NullReferenceException(); diff --git a/tests/Elastic.Markdown.Tests/DocSet/NavigationTestsBase.cs b/tests/Elastic.Markdown.Tests/DocSet/NavigationTestsBase.cs index 18b27d1c3..658aa6927 100644 --- a/tests/Elastic.Markdown.Tests/DocSet/NavigationTestsBase.cs +++ b/tests/Elastic.Markdown.Tests/DocSet/NavigationTestsBase.cs @@ -4,8 +4,10 @@ using System.IO.Abstractions; using System.IO.Abstractions.TestingHelpers; +using Elastic.Documentation; using Elastic.Documentation.Configuration; using Elastic.Documentation.Configuration.Builder; +using Elastic.Documentation.Configuration.Versions; using Elastic.Markdown.IO; using FluentAssertions; using Microsoft.Extensions.Logging; @@ -23,7 +25,21 @@ protected NavigationTestsBase(ITestOutputHelper output) CurrentDirectory = Paths.WorkingDirectoryRoot.FullName }); var collector = new TestDiagnosticsCollector(output); - var context = new BuildContext(collector, ReadFileSystem, WriteFileSystem) + var versionsConfig = new VersionsConfiguration + { + VersioningSystems = new Dictionary + { + { + VersioningSystemId.Stack, new VersioningSystem + { + Id = VersioningSystemId.Stack, + Current = new SemVersion(8, 0, 0), + Base = new SemVersion(8, 0, 0) + } + } + } + }; + var context = new BuildContext(collector, ReadFileSystem, WriteFileSystem, versionsConfig) { Force = false, UrlPathPrefix = null diff --git a/tests/Elastic.Markdown.Tests/Inline/InlneBaseTests.cs b/tests/Elastic.Markdown.Tests/Inline/InlneBaseTests.cs index 1bb7b6f11..6699e2fe4 100644 --- a/tests/Elastic.Markdown.Tests/Inline/InlneBaseTests.cs +++ b/tests/Elastic.Markdown.Tests/Inline/InlneBaseTests.cs @@ -3,7 +3,9 @@ // See the LICENSE file in the project root for more information using System.IO.Abstractions.TestingHelpers; using System.Runtime.InteropServices; +using Elastic.Documentation; using Elastic.Documentation.Configuration; +using Elastic.Documentation.Configuration.Versions; using Elastic.Markdown.IO; using FluentAssertions; using JetBrains.Annotations; @@ -112,7 +114,21 @@ protected InlineTest( FileSystem.GenerateDocSetYaml(root, globalVariables); Collector = new TestDiagnosticsCollector(output); - var context = new BuildContext(Collector, FileSystem) + var versionsConfig = new VersionsConfiguration + { + VersioningSystems = new Dictionary + { + { + VersioningSystemId.Stack, new VersioningSystem + { + Id = VersioningSystemId.Stack, + Current = new SemVersion(8, 0, 0), + Base = new SemVersion(8, 0, 0) + } + } + } + }; + var context = new BuildContext(Collector, FileSystem, versionsConfig) { UrlPathPrefix = "/docs" }; diff --git a/tests/Elastic.Markdown.Tests/OutputDirectoryTests.cs b/tests/Elastic.Markdown.Tests/OutputDirectoryTests.cs index 132505228..55d83ee60 100644 --- a/tests/Elastic.Markdown.Tests/OutputDirectoryTests.cs +++ b/tests/Elastic.Markdown.Tests/OutputDirectoryTests.cs @@ -2,7 +2,9 @@ // 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 Elastic.Documentation; using Elastic.Documentation.Configuration; +using Elastic.Documentation.Configuration.Versions; using Elastic.Documentation.Diagnostics; using Elastic.Markdown.IO; using FluentAssertions; @@ -30,7 +32,21 @@ public async Task CreatesDefaultOutputDirectory() CurrentDirectory = Paths.WorkingDirectoryRoot.FullName }); await using var collector = new DiagnosticsCollector([]).StartAsync(TestContext.Current.CancellationToken); - var context = new BuildContext(collector, fileSystem); + var versionsConfig = new VersionsConfiguration + { + VersioningSystems = new Dictionary + { + { + VersioningSystemId.Stack, new VersioningSystem + { + Id = VersioningSystemId.Stack, + Current = new SemVersion(8, 0, 0), + Base = new SemVersion(8, 0, 0) + } + } + } + }; + var context = new BuildContext(collector, fileSystem, versionsConfig); var linkResolver = new TestCrossLinkResolver(); var set = new DocumentationSet(context, logger, linkResolver); var generator = new DocumentationGenerator(set, logger); diff --git a/tests/authoring/Applicability/AppliesToDirective.fs b/tests/authoring/Applicability/AppliesToDirective.fs index 0bf4bfc8e..91960bd83 100644 --- a/tests/authoring/Applicability/AppliesToDirective.fs +++ b/tests/authoring/Applicability/AppliesToDirective.fs @@ -17,7 +17,7 @@ type ``piggy back off yaml formatting`` () = serverless: security: ga 9.0.0 elasticsearch: beta 9.1.0 - observability: discontinued 9.2.0 + observability: removed 9.2.0 ``` """ @@ -30,7 +30,7 @@ serverless: Serverless=ServerlessProjectApplicability( Security=AppliesCollection.op_Explicit "ga 9.0.0", Elasticsearch=AppliesCollection.op_Explicit "beta 9.1.0", - Observability=AppliesCollection.op_Explicit "discontinued 9.2.0" + Observability=AppliesCollection.op_Explicit "removed 9.2.0" ) )) @@ -40,7 +40,7 @@ type ``plain block`` () = serverless: security: ga 9.0.0 elasticsearch: beta 9.1.0 - observability: discontinued 9.2.0 + observability: removed 9.2.0 apm_agent_dotnet: ga 9.0 apm_agent_node: ga 10.0 ``` @@ -55,7 +55,7 @@ apm_agent_node: ga 10.0 Serverless=ServerlessProjectApplicability( Security=AppliesCollection.op_Explicit "ga 9.0.0", Elasticsearch=AppliesCollection.op_Explicit "beta 9.1.0", - Observability=AppliesCollection.op_Explicit "discontinued 9.2.0" + Observability=AppliesCollection.op_Explicit "removed 9.2.0" ), ProductApplicability=ProductApplicability( ApmAgentDotnet=AppliesCollection.op_Explicit "ga 9.0", diff --git a/tests/authoring/Applicability/AppliesToFrontMatter.fs b/tests/authoring/Applicability/AppliesToFrontMatter.fs index f6a8f6aab..d6682ca5f 100644 --- a/tests/authoring/Applicability/AppliesToFrontMatter.fs +++ b/tests/authoring/Applicability/AppliesToFrontMatter.fs @@ -60,7 +60,7 @@ applies_to: serverless: security: ga 9.0.0 elasticsearch: beta 9.1.0 - observability: discontinued 9.2.0 + observability: removed 9.2.0 """ [] let ``apply matches expected`` () = @@ -68,7 +68,7 @@ applies_to: Serverless=ServerlessProjectApplicability( Security=AppliesCollection.op_Explicit "ga 9.0.0", Elasticsearch=AppliesCollection.op_Explicit "beta 9.1.0", - Observability=AppliesCollection.op_Explicit "discontinued 9.2.0" + Observability=AppliesCollection.op_Explicit "removed 9.2.0" ) )) @@ -106,7 +106,7 @@ applies_to: deployment: eck: ga 9.0 ess: beta 9.1 - ece: discontinued 9.2.0 + ece: removed 9.2.0 self: unavailable 9.3.0 """ [] @@ -115,7 +115,7 @@ applies_to: Deployment=DeploymentApplicability( Eck=AppliesCollection.op_Explicit "ga 9.0", Ess=AppliesCollection.op_Explicit "beta 9.1", - Ece=AppliesCollection.op_Explicit "discontinued 9.2.0", + Ece=AppliesCollection.op_Explicit "removed 9.2.0", Self=AppliesCollection.op_Explicit "unavailable 9.3.0" ) )) @@ -157,14 +157,14 @@ applies_to: type ``parses product multiple`` () = static let markdown = frontMatter """ applies_to: - product: preview 9.5, discontinued 9.7 + product: preview 9.5, removed 9.7 """ [] let ``apply matches expected`` () = markdown |> appliesTo (ApplicableTo( Product=AppliesCollection([ Applicability.op_Explicit "preview 9.5"; - Applicability.op_Explicit "discontinued 9.7" + Applicability.op_Explicit "removed 9.7" ] |> Array.ofList) )) @@ -173,12 +173,12 @@ type ``lenient to defining types at top level`` () = applies_to: eck: ga 9.0 ess: beta 9.1 - ece: discontinued 9.2.0 + ece: removed 9.2.0 self: unavailable 9.3.0 security: ga 9.0.0 elasticsearch: beta 9.1.0 - observability: discontinued 9.2.0 - product: preview 9.5, discontinued 9.7 + observability: removed 9.2.0 + product: preview 9.5, removed 9.7 apm_agent_dotnet: ga 9.0 ecctl: ga 10.0 stack: ga 9.1 @@ -189,16 +189,16 @@ applies_to: Deployment=DeploymentApplicability( Eck=AppliesCollection.op_Explicit "ga 9.0", Ess=AppliesCollection.op_Explicit "beta 9.1", - Ece=AppliesCollection.op_Explicit "discontinued 9.2.0", + Ece=AppliesCollection.op_Explicit "removed 9.2.0", Self=AppliesCollection.op_Explicit "unavailable 9.3.0" ), Serverless=ServerlessProjectApplicability( Security=AppliesCollection.op_Explicit "ga 9.0.0", Elasticsearch=AppliesCollection.op_Explicit "beta 9.1.0", - Observability=AppliesCollection.op_Explicit "discontinued 9.2.0" + Observability=AppliesCollection.op_Explicit "removed 9.2.0" ), Stack=AppliesCollection.op_Explicit "ga 9.1.0", - Product=AppliesCollection.op_Explicit "preview 9.5, discontinued 9.7", + Product=AppliesCollection.op_Explicit "preview 9.5, removed 9.7", ProductApplicability=ProductApplicability( ApmAgentDotnet=AppliesCollection.op_Explicit "ga 9.0", Ecctl=AppliesCollection.op_Explicit "ga 10.0" diff --git a/tests/authoring/Framework/Setup.fs b/tests/authoring/Framework/Setup.fs index eedce1583..406f2dc8f 100644 --- a/tests/authoring/Framework/Setup.fs +++ b/tests/authoring/Framework/Setup.fs @@ -10,7 +10,9 @@ open System.Collections.Generic open System.IO open System.IO.Abstractions.TestingHelpers open System.Threading.Tasks +open Elastic.Documentation open Elastic.Documentation.Configuration +open Elastic.Documentation.Configuration.Versions open Elastic.Markdown open Elastic.Markdown.IO open JetBrains.Annotations @@ -53,11 +55,11 @@ type Setup = ) = let root = fileSystem.DirectoryInfo.New(Path.Combine(Paths.WorkingDirectoryRoot.FullName, "docs/")); let yaml = new StringWriter(); - yaml.WriteLine("cross_links:"); - yaml.WriteLine(" - docs-content"); - yaml.WriteLine(" - elasticsearch"); + yaml.WriteLine("cross_links:") + yaml.WriteLine(" - docs-content") + yaml.WriteLine(" - elasticsearch") yaml.WriteLine(" - kibana") - yaml.WriteLine("toc:"); + yaml.WriteLine("toc:") let markdownFiles = fileSystem.Directory.EnumerateFiles(root.FullName, "*.md", SearchOption.AllDirectories) markdownFiles |> Seq.iter(fun markdownFile -> @@ -108,7 +110,107 @@ type Setup = GenerateDocSetYaml (fileSystem, None) let collector = TestDiagnosticsCollector() - let context = BuildContext(collector, fileSystem, UrlPathPrefix=(options.UrlPathPrefix |> Option.defaultValue "")) + let versioningSystems = Dictionary() + versioningSystems.Add(VersioningSystemId.Stack, + VersioningSystem( + Id = VersioningSystemId.Stack, + Current = SemVersion(8, 0, 0), + Base = SemVersion(8, 0, 0) + ) + ) + versioningSystems.Add(VersioningSystemId.Self, + VersioningSystem( + Id = VersioningSystemId.Self, + Current = SemVersion(8, 0, 0), + Base = SemVersion(8, 0, 0) + ) + ) + versioningSystems.Add(VersioningSystemId.Ece, + VersioningSystem( + Id = VersioningSystemId.Ece, + Current = SemVersion(8, 0, 0), + Base = SemVersion(8, 0, 0) + ) + ) + versioningSystems.Add(VersioningSystemId.Eck, + VersioningSystem( + Id = VersioningSystemId.Eck, + Current = SemVersion(8, 0, 0), + Base = SemVersion(8, 0, 0) + ) + ) + versioningSystems.Add(VersioningSystemId.Ech, + VersioningSystem( + Id = VersioningSystemId.Ech, + Current = SemVersion(8, 0, 0), + Base = SemVersion(8, 0, 0) + ) + ) + versioningSystems.Add(VersioningSystemId.ApmAgentDotnet, + VersioningSystem( + Id = VersioningSystemId.ApmAgentDotnet, + Current = SemVersion(8, 0, 0), + Base = SemVersion(8, 0, 0) + ) + ) + versioningSystems.Add(VersioningSystemId.ApmAgentNode, + VersioningSystem( + Id = VersioningSystemId.ApmAgentNode, + Current = SemVersion(8, 0, 0), + Base = SemVersion(8, 0, 0) + ) + ) + versioningSystems.Add(VersioningSystemId.Ecctl, + VersioningSystem( + Id = VersioningSystemId.Ecctl, + Current = SemVersion(8, 0, 0), + Base = SemVersion(8, 0, 0) + ) + ) + versioningSystems.Add(VersioningSystemId.Elasticsearch, + VersioningSystem( + Id = VersioningSystemId.Elasticsearch, + Current = SemVersion(8, 0, 0), + Base = SemVersion(8, 0, 0) + ) + ) + versioningSystems.Add(VersioningSystemId.Ess, + VersioningSystem( + Id = VersioningSystemId.Ess, + Current = SemVersion(8, 0, 0), + Base = SemVersion(8, 0, 0) + ) + ) + versioningSystems.Add(VersioningSystemId.All, + VersioningSystem( + Id = VersioningSystemId.All, + Current = SemVersion(8, 0, 0), + Base = SemVersion(8, 0, 0) + ) + ) + versioningSystems.Add(VersioningSystemId.Observability, + VersioningSystem( + Id = VersioningSystemId.Observability, + Current = SemVersion(8, 0, 0), + Base = SemVersion(8, 0, 0) + ) + ) + versioningSystems.Add(VersioningSystemId.Security, + VersioningSystem( + Id = VersioningSystemId.Security, + Current = SemVersion(8, 0, 0), + Base = SemVersion(8, 0, 0) + ) + ) + versioningSystems.Add(VersioningSystemId.Serverless, + VersioningSystem( + Id = VersioningSystemId.Serverless, + Current = SemVersion(8, 0, 0), + Base = SemVersion(8, 0, 0) + ) + ) + let versionConfig = VersionsConfiguration(VersioningSystems = versioningSystems) + let context = BuildContext(collector, fileSystem, UrlPathPrefix=(options.UrlPathPrefix |> Option.defaultValue ""), versionsConfig = versionConfig) let logger = new TestLoggerFactory() let conversionCollector = TestConversionCollector() let linkResolver = TestCrossLinkResolver(context.Configuration) @@ -145,4 +247,4 @@ type Setup = lazy ( task { return! Setup.Generator [Index m] None } |> Async.AwaitTask |> Async.RunSynchronously - ) \ No newline at end of file + ) diff --git a/tests/authoring/Inline/AppliesToRole.fs b/tests/authoring/Inline/AppliesToRole.fs index b5fcb830a..e5583c25f 100644 --- a/tests/authoring/Inline/AppliesToRole.fs +++ b/tests/authoring/Inline/AppliesToRole.fs @@ -28,19 +28,21 @@ This is an inline {applies_to}`stack: preview 9.1` element. [] let ``validate HTML: generates link and alt attr`` () = markdown |> convertsToHtml """ -

This is an inline - - - Elastic Stack - - - Planned - - - - - element.

- """ +

This is an inline + + + Elastic Stack + + + Planned + + + + + element.

+""" type ``parses nested ess moniker`` () = @@ -72,3 +74,51 @@ This is an inline {preview}`9.1` element. directives |> appliesToDirective (ApplicableTo( Product=AppliesCollection.op_Explicit "preview 9.1.0" )) + + +type ``parses applies to without version in table`` () = + static let markdown = Setup.Markdown """ +| col1 | col2 | +|------|------------------------------| +| test | {applies_to}`ece: removed` | +""" + + [] + let ``parses to AppliesDirective`` () = + let directives = markdown |> converts "index.md" |> parses + test <@ directives.Length = 1 @> + directives |> appliesToDirective (ApplicableTo( + Deployment=DeploymentApplicability( + Ece=AppliesCollection.op_Explicit "removed" + ) + )) + +type ``parses applies to with text afterwards`` () = + static let markdown = Setup.Markdown """ +{applies_to}`ece: removed` hello world +""" + + [] + let ``parses to AppliesDirective`` () = + let directives = markdown |> converts "index.md" |> parses + test <@ directives.Length = 1 @> + directives |> appliesToDirective (ApplicableTo( + Deployment=DeploymentApplicability( + Ece=AppliesCollection.op_Explicit "removed" + ) + )) + +type ``parses multiple applies_to in one line`` () = + static let markdown = Setup.Markdown """ +{applies_to}`ece: removed` {applies_to}`ece: removed` +""" + + [] + let ``parses to AppliesDirective`` () = + let directives = markdown |> converts "index.md" |> parses + test <@ directives.Length = 2 @> + directives |> appliesToDirective (ApplicableTo( + Deployment=DeploymentApplicability( + Ece=AppliesCollection.op_Explicit "removed" + ) + ))