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