@redpanda-data/docs-extensions-and-macros 5.11.1 → 5.12.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2,7 +2,12 @@
2
2
 
3
3
  const { raiseListenerLimit } = require('./util/raise-listener-limit')
4
4
 
5
- const semver = require('semver')
5
+ // The same comparator the prop macro uses to pick a dataset. semver.coerce
6
+ // keeps 'rc10' as one alphanumeric identifier and string-compares it, so it
7
+ // ranked v26.3.1-rc2 above v26.3.1-rc10 while the macro ranked them correctly:
8
+ // build-time validation and the runtime tooltip fetch would then read different
9
+ // files. Sharing the comparator is the only way they cannot disagree.
10
+ const { compareTags } = require('../macros/prop')
6
11
 
7
12
  /**
8
13
  * Points UI templates at generated JSON attachments that actually exist.
@@ -19,7 +24,9 @@ const semver = require('semver')
19
24
  * component version:
20
25
  *
21
26
  * - available-properties-tag: newest redpanda-properties-<tag>.json attachment
22
- * in the reference module of each streaming (ROOT) component version
27
+ * in the reference module of a component version that publishes property
28
+ * data (the ROOT and streaming components, plus any component shipping such
29
+ * an attachment of its own)
23
30
  * - available-connect-version: newest connect-<version>.json attachment in the
24
31
  * components module of the connect component
25
32
  * - page-disable-property-tooltips: 'true' on streaming versions that have no
@@ -29,6 +36,12 @@ const semver = require('semver')
29
36
  * overwritten.
30
37
  */
31
38
 
39
+ // Components known to publish Redpanda property data. Any other component that
40
+ // ships a redpanda-properties-<tag>.json attachment is picked up too: the docs
41
+ // UI resolves the tooltip dataset against the page's own component when that
42
+ // component declares available-properties-tag, so a component publishing its
43
+ // own data must get the attribute or its tooltips fall back to streaming's and
44
+ // describe different properties from the ones its pages document.
32
45
  const PROPERTIES_COMPONENTS = ['ROOT', 'streaming']
33
46
  const CONNECT_COMPONENTS = ['connect', 'redpanda-connect']
34
47
 
@@ -41,13 +54,26 @@ module.exports.register = function () {
41
54
 
42
55
  this.once('contentClassified', ({ contentCatalog }) => {
43
56
  contentCatalog.getComponents().forEach((component) => {
44
- const isProperties = PROPERTIES_COMPONENTS.includes(component.name)
57
+ const publishesProperties = (contentCatalog.findBy({ component: component.name, family: 'attachment' }) || [])
58
+ .some((file) => file.src.module === 'reference' && PROPERTIES_JSON_RX.test(file.src.relative.split('/').pop()))
59
+ const isProperties = PROPERTIES_COMPONENTS.includes(component.name) || publishesProperties
45
60
  const isConnect = CONNECT_COMPONENTS.includes(component.name)
46
61
  if (!isProperties && !isConnect) return
47
62
 
48
63
  component.versions.forEach((compVer) => {
49
- const attributes = ((compVer.asciidoc = compVer.asciidoc || {}).attributes =
50
- compVer.asciidoc.attributes || {})
64
+ // Copy before writing. Antora hands every component version whose
65
+ // antora.yml declares no `asciidoc:` block THE SAME siteAsciiDocConfig
66
+ // object -- see the NOTE at content-classifier/lib/content-catalog.js
67
+ // ("if no AsciiDoc attributes are defined in the component descriptor,
68
+ // asciidoc is the siteAsciiDocConfig object"). Mutating it in place
69
+ // wrote these attributes into the shared site config, so a component
70
+ // publishing no property data still advertised a dataset, and because
71
+ // the write is guarded on the attribute being unset, the first version
72
+ // processed won and every later version silently kept that value --
73
+ // no log line, and the wrong tag. Cloning both levels makes the write
74
+ // per-version, which is what the attribute means.
75
+ const asciidoc = (compVer.asciidoc = { ...compVer.asciidoc })
76
+ const attributes = (asciidoc.attributes = { ...asciidoc.attributes })
51
77
  const attachments = contentCatalog.findBy({
52
78
  component: component.name,
53
79
  version: compVer.version,
@@ -90,17 +116,13 @@ module.exports.register = function () {
90
116
 
91
117
  function findNewestVersion (attachments, module, rx) {
92
118
  let newestRaw = null
93
- let newestSemver = null
94
119
  attachments.forEach((attachment) => {
95
120
  if (attachment.src.module !== module) return
96
121
  const basename = attachment.src.relative.split('/').pop()
97
122
  const match = basename.match(rx)
98
123
  if (!match) return
99
124
  const raw = match[1]
100
- const parsed = semver.coerce(raw, { includePrerelease: true })
101
- if (!parsed) return
102
- if (!newestSemver || semver.gt(parsed, newestSemver)) {
103
- newestSemver = parsed
125
+ if (!newestRaw || compareTags(raw, newestRaw) > 0) {
104
126
  newestRaw = raw
105
127
  }
106
128
  })
@@ -0,0 +1,188 @@
1
+ = Documenting properties and enterprise features before they ship
2
+ :toc:
3
+ :toclevels: 3
4
+
5
+ How to reference a configuration property or an enterprise feature that is not in a released version of Redpanda yet, so that the build checks your work instead of you having to remember which release something landed in.
6
+
7
+ For how the macros work generally, see link:PROPERTY_AND_ENTERPRISE_REFERENCES.adoc[the explainer] and link:REFERENCE.adoc[the macros reference].
8
+
9
+ == The two situations
10
+
11
+ Redpanda cuts release candidates before every release, and the docs track them on a prerelease branch (`beta`) that publishes under `/beta/` on the site. Two different things can be true of a feature or property on that branch:
12
+
13
+ *Unreleased.* It exists only in a release candidate. Nobody outside can get it yet. You are writing the docs ahead of the release so they ship with it.
14
+
15
+ *Released beta.* It is publicly available, but as a beta: real users can turn it on, and it may still change.
16
+
17
+ The distinction matters because it decides where a mention is allowed. An unreleased thing referenced from released docs promises readers something they cannot have. A released beta is fine to mention anywhere, as long as readers can see it is a beta.
18
+
19
+ The macros enforce that distinction, so the rule you need to remember is short: *write the mention on the branch the feature is actually on, and let the build tell you if you got it wrong.*
20
+
21
+ == Properties in a release candidate
22
+
23
+ === What has to exist first
24
+
25
+ A property mention is only as good as the property data behind it. Each release series publishes its own `redpanda-properties-v<major>.<minor>.<patch>.json` as a reference-module attachment, and a page validates against the file for *its own* series. A prerelease branch is no exception: it needs a file generated from the RC tag.
26
+
27
+ [,bash]
28
+ ----
29
+ # On the beta branch, from the docs repo
30
+ npx doc-tools generate property-docs --tag v26.3.1-rc1
31
+ ----
32
+
33
+ `--tag` accepts release-candidate tags. The generated file is `redpanda-properties-v26.3.1-rc1.json`, and because its series is `26.3` it is picked up by a component version at `version: 26.3`.
34
+
35
+ Regenerate it for each RC. Tags compare by release precedence, so `rc2` supersedes `rc1` and the GA `v26.3.1` supersedes both -- you can leave older RC files in place and the build will use the newest.
36
+
37
+ === Writing the mention
38
+
39
+ Once that file is committed on the branch, nothing special is needed. Write the macro as you would anywhere:
40
+
41
+ [,asciidoc]
42
+ ----
43
+ Set prop:my_new_property[] to enable the thing.
44
+ ----
45
+
46
+ The build validates the name against the RC data. A typo gets a warning naming the file it checked. A property that only exists in the RC validates on the beta branch and, correctly, fails validation if someone copies the sentence to a released branch, because it is not in that release's data.
47
+
48
+ === If you forget the property data
49
+
50
+ You get told, once per component version, with the command to run:
51
+
52
+ [literal]
53
+ ----
54
+ prop macro: streaming@26.3 publishes no redpanda-properties-v26.3.*.json attachment
55
+ in its reference module (found for other series: v26.2.1). prop: targets on 26.3
56
+ pages are therefore not validated and their tooltips carry no data, rather than
57
+ being checked against another release's properties. Generate it with
58
+ 'npx doc-tools generate property-docs --tag v26.3.<patch>' on that branch, or
59
+ leave prop: out of 26.3 content.
60
+ ----
61
+
62
+ Mentions render as plain code with no tooltip until the file exists. That is deliberate: validating 26.3 content against 26.2 data would accept properties that release never had and flag removed ones as typos.
63
+
64
+ === Cloud pages and release candidates
65
+
66
+ Cloud runs released Redpanda, so Cloud pages always validate against the newest *GA* property data and ignore release candidates entirely. You cannot document an RC-only property on a Cloud page, and you should not want to -- the property is not in Cloud until the release ships and Cloud picks it up.
67
+
68
+ A property is available to Cloud only when its `cloud_supported` field is `true`, which comes from the Cloud support data the extractor fetches. For a property new in an RC that flag is `false`, so a Cloud mention renders as plain code with a warning. When Cloud gains the property, regenerating the data flips the flag and the mention starts working with no docs change.
69
+
70
+ === Single-sourced pages need a second look
71
+
72
+ A Cloud page that is a stub including a self-managed page inherits that page's `prop:` calls, and they are then checked against Cloud's surface. A self-managed-only property therefore renders as plain code on the Cloud rendering of shared prose, with a warning naming the Cloud page even though the mention lives in the included file.
73
+
74
+ That is the macro doing its job: the shared sentence is telling a Cloud reader to set something they cannot set. Fix the prose rather than the macro -- usually by moving the instruction inside an `ifndef::env-cloud[]` block.
75
+
76
+ == Enterprise features in a release candidate
77
+
78
+ Enterprise features are declared in the registry (`enterprise-features.yml` in the `shared` component), and the release status lives there rather than in each mention. That way one edit updates every reference and every generated licensing table.
79
+
80
+ === Declaring the status
81
+
82
+ [,yaml]
83
+ ----
84
+ # Only in a release candidate: not public yet.
85
+ - name: My Upcoming Feature
86
+ scope: redpanda
87
+ status: unreleased
88
+ description: What it does.
89
+ expiration: What happens when the licence expires.
90
+
91
+ # Publicly available as a beta.
92
+ - name: My Beta Feature
93
+ scope: redpanda
94
+ status: beta
95
+ description: What it does.
96
+ expiration: What happens when the licence expires.
97
+ ----
98
+
99
+ `status` is one of:
100
+
101
+ [cols="1,3a"]
102
+ |===
103
+ | Value | Meaning
104
+
105
+ | `ga`
106
+ | Shipped. The default when `status` is absent, so existing entries need no change. Referenced anywhere, no badge.
107
+
108
+ | `beta`
109
+ | Publicly available as a beta. Referenced anywhere. Renders a `beta` badge in prose and in the licensing tables.
110
+
111
+ | `unreleased`
112
+ | In a release candidate only. Referenced *only* from prerelease docs. Renders an `unreleased` badge there, and is omitted from licensing tables on released docs.
113
+ |===
114
+
115
+ `beta: true` is the older spelling and still means `status: beta`. Prefer `status` in new entries: it can express "not public at all", which a boolean cannot.
116
+
117
+ Add `status-tooltip` to override the hover text on an `unreleased` badge, or `beta-tooltip` on a `beta` badge.
118
+
119
+ === Writing the mention
120
+
121
+ Unchanged:
122
+
123
+ [,asciidoc]
124
+ ----
125
+ enterprise:My Upcoming Feature[] requires a licence.
126
+ ----
127
+
128
+ On the prerelease branch this renders with the `unreleased` badge. On released docs it renders as plain text with no enterprise styling, no tooltip, and no link, plus a warning:
129
+
130
+ [literal]
131
+ ----
132
+ enterprise:My Upcoming Feature[] in modules/manage/pages/example.adoc:
133
+ 'My Upcoming Feature' is marked status: unreleased in the enterprise features
134
+ registry, so it is only documented for an upcoming release. This page belongs to
135
+ a released version, so the mention renders as plain text with no enterprise
136
+ styling, tooltip, or link. Move the mention to the prerelease (beta) branch, or
137
+ change the status once the feature ships.
138
+ ----
139
+
140
+ The `unreleased` badge cannot be suppressed with `enterprise-beta-badge=false`. "Not in a release yet" is information a reader needs, not decoration.
141
+
142
+ === Licensing tables
143
+
144
+ `enterprise_features::<scope>[]` omits `unreleased` features on released docs, because the table states what a licence covers today and listing something unavailable makes it wrong. On prerelease docs the same table lists them with their badge.
145
+
146
+ == When the release ships
147
+
148
+ Two edits, in this order:
149
+
150
+ . *Properties.* Generate the GA property data on the branch that becomes the released version:
151
+ +
152
+ [,bash]
153
+ ----
154
+ npx doc-tools generate property-docs --tag v26.3.1
155
+ ----
156
+ +
157
+ The GA tag supersedes its release candidates automatically, so no mention changes. Cloud starts seeing the new properties when the Cloud support data marks them `cloud_supported`.
158
+
159
+ . *Enterprise features.* Change `status: unreleased` to `status: beta` (if it ships as a public beta) or remove the `status` field (if it ships generally). Every mention and every table updates from that one edit.
160
+
161
+ Nothing in prose needs touching for either.
162
+
163
+ == Checklist
164
+
165
+ Before opening a PR that documents something unreleased:
166
+
167
+ * [ ] The mention is on the prerelease branch, not on `main` or a released `v/*` branch.
168
+ * [ ] For a property: the RC property JSON is committed on that branch, generated from the RC tag.
169
+ * [ ] For an enterprise feature: the registry entry has `status: unreleased`, not `beta: true`, unless it is genuinely a public beta.
170
+ * [ ] The build log has no `prop macro:` or `enterprise:` warnings for your additions.
171
+ * [ ] No mention of an unreleased property or feature on a Cloud page.
172
+
173
+ == Seeing it work
174
+
175
+ The preview site in this repository has fixtures for every case above, including a prerelease component version and a Cloud component:
176
+
177
+ [,bash]
178
+ ----
179
+ ./scripts/macro-test.sh --serve
180
+ ----
181
+
182
+ Open `/preview/macros-showcase/`, which shows each macro call, what it renders, and why. `/preview/release-status-test/` and `/macro-fixtures/26.3/` show the same unreleased feature gated on a released version and badged on a prerelease one.
183
+
184
+ == Why the build enforces this rather than trusting reviewers
185
+
186
+ Every one of these rules is checkable from data the build already has: which release series a page belongs to, whether its component version is a prerelease, which properties that release actually defines, and what the registry says about a feature's status. A reviewer would have to hold all of that in their head across hundreds of mentions and nine doc versions.
187
+
188
+ So the macros check it, and the failure mode is always the same: render the plain name and say why. That is deliberate. A wrong tooltip or a link to a page describing a feature nobody can get is worse than no tooltip at all, because the reader has no way to tell it is wrong.
@@ -23,13 +23,15 @@ prop:cloud_storage_enabled[]
23
23
 
24
24
  That's it. Here's what happens for you:
25
25
 
26
- *It's checked.* The name is looked up in the published property data -- the same JSON, generated from Redpanda's source code, that powers the reference pages and tooltips. A typo gets a build warning with the file name and a "did you mean" suggestion. Nothing unverified ships. (A site can turn this up to a hard build failure with the `property-validate` attribute.)
26
+ *It's checked, against the right release.* The name is looked up in the published property data -- the same JSON, generated from Redpanda's source code, that powers the reference pages and tooltips. A typo gets a build warning naming the file, the release data it was checked against, and a "did you mean" suggestion. Nothing unverified ships. (A site can turn this up to a hard build failure with the `property-validate` attribute.)
27
+
28
+ Every release series has its own copy of that data, and the build picks the copy matching the page's own version: a 25.3 page is checked against 25.3 properties, never against the newest. Otherwise a property added in 26.2 would pass silently on a 25.3 page, and one removed since 25.3 would be flagged there as a typo. If a version branch has no property data of its own, the build says so and skips validation for that version rather than substituting a neighbouring release's -- and names the command that generates the missing file.
27
29
 
28
30
  *It gets a tooltip.* The docs UI decorates only elements the macro marked. A plain backticked word never gets a tooltip anymore, so `admin` in a Helm example stays plain text unless a writer deliberately writes `prop:admin[]`.
29
31
 
30
32
  *It can link itself.* Add `link=true` and the property links to its reference page. You don't say which page -- the build works that out by looking at which reference page actually documents that property (details below). If our property reference is ever reorganized into different pages, every link follows automatically.
31
33
 
32
- *It adapts to Kubernetes pages.* Add `helm-path=auto` and, on pages rendered for Kubernetes, the property displays as the Helm values path readers actually type -- `config.cluster.log_segment_size`, `config.node.fips_mode`, or `storage.tiered.config.cloud_storage_enabled` for tiered storage settings. On Linux pages the same call shows the plain name. One sentence, correct for both audiences.
34
+ *It adapts to Kubernetes pages.* Add `helm-path=auto` and, on pages rendered for Kubernetes, the property displays as the Helm values path readers actually type -- `config.cluster.log_segment_size`, `config.node.fips_mode`, or `storage.tiered.config.cloud_storage_enabled` for tiered storage settings. On Linux pages the same call shows the plain name, as does a topic property anywhere: topic properties are set per topic rather than in `values.yaml`, so there is no path to show. One sentence, correct for both audiences.
33
35
 
34
36
  *Once per paragraph is enough.* Mark the first mention of a property in a paragraph and leave later mentions as plain backticks -- a paragraph doesn't need three identical tooltips. The migration tool follows this rule, and the UI enforces it as a backstop: even if every mention is marked, only the first one per paragraph gets the tooltip.
35
37
 
@@ -37,10 +39,28 @@ That's it. Here's what happens for you:
37
39
 
38
40
  Rather than hardcoding "cluster properties live on the cluster-properties page," the macro reads the actual docs during the build. Every documented property appears as a heading in the generated property files, and each reference page pulls in one or more of those files. The macro follows that chain -- which pages exist, what they include, which property headings end up where -- and builds a lookup table per doc set.
39
41
 
40
- Because the lookup is done separately for each part of the site, a Cloud page links to Cloud's own property pages and a streaming page to streaming's. Cloud reuses streaming's property files with a filter ("only the cloud-relevant parts"), and the macro applies that same filter, so it never links to a property that a given page doesn't actually document. If a doc set doesn't cover a property at all -- Cloud publishes no topic-properties page, Connect publishes none -- the link borrows the streaming docs instead of pointing nowhere.
42
+ Because the lookup is done separately for each part of the site, a Cloud page links to Cloud's own property pages and a streaming page to streaming's, and a link is only ever emitted when a page in that same doc set actually documents the property. Nothing is linked across doc sets, and no page is guessed from a property's scope -- a link the build can't verify is a broken link.
43
+
44
+ Availability is a separate question from linking, and it comes from the data rather than from which pages happen to include what. Cloud is managed Redpanda, so it has no property release of its own: a Cloud page reads the newest *released* self-managed file, ignoring release candidates, because Cloud runs released Redpanda.
45
+
46
+ What that file can rule out is narrower than it looks. `cloud_supported` is derived from the Cloud control plane's install pack, and that source describes *cluster* configuration and nothing else. So the gate applies to cluster properties only: a cluster property the control plane forbids renders as plain code -- no link and no tooltip -- plus a build warning naming the property and the file, because a tooltip there would tell a Cloud reader they can change something they can't. For any other scope the file has no opinion, and a mention is allowed. Topic properties in particular are set per topic through the Kafka API, rpk, or Console -- a surface the control plane doesn't gate -- and Cloud publishes a topic properties reference of its own.
47
+
48
+ An absent `cloud_supported` therefore means "not asserted", not "unsupported". Only an explicit `false` on a cluster property blocks a mention. (Where the flag is written, it is self-consistent: no published property is editable or read-only in Cloud without also being supported.)
49
+
50
+ Doc sets with no property data -- Connect, the agentic data plane -- get nothing, and nothing is inferred for them. Their version numbers are their own product's, and say nothing about which Redpanda properties exist. Adding a third doc set is a deliberate change to the macro, not something it guesses.
51
+
52
+ An available property that no page *renders* is a different situation, and keeps its tooltip. Deprecated properties are the usual case: the reference pages filter them out, but the published data still describes them accurately, so a tooltip on the deprecated-properties page is exactly what a reader wants. There's no page to link, so `link=true` renders unlinked and warns; a tooltip-only mention is silent.
41
53
 
42
54
  Think phone book, not memory: if a property moves, the next build's phone book is already right.
43
55
 
56
+ === Features and properties that have not shipped yet
57
+
58
+ Redpanda cuts release candidates before every release, and the docs track them on a prerelease branch. Something in an RC is not the same as something in a public beta, and the difference decides where a mention is allowed: an unreleased thing referenced from released docs promises readers something they cannot have.
59
+
60
+ Both macros enforce that from data rather than from reviewer memory. A property validates against its own release series, so an RC-only property works on the beta branch and fails on a released one. An enterprise feature carries `status: unreleased` or `status: beta` in the registry, and an unreleased feature referenced from released docs renders as plain text with a warning, and is omitted from licensing tables there. Cloud always uses GA property data and ignores release candidates entirely, because Cloud runs released Redpanda.
61
+
62
+ The writer-facing procedure, including how to generate property data from an RC tag, is in link:DOCUMENTING_UNRELEASED_FEATURES.adoc[Documenting properties and enterprise features before they ship].
63
+
44
64
  === What happened to config_ref
45
65
 
46
66
  The old `config_ref` macro did a smaller version of this job with three problems: you had to tell it which page to link to by hand, it never checked the property name, and on Kubernetes pages it prefixed *every* property with `storage.tiered.config.` -- which is only correct for tiered storage settings. The live docs really did render `storage.tiered.config.log_segment_size`, a path that mislabels where that setting belongs.
@@ -20,7 +20,7 @@ antora:
20
20
 
21
21
  == config_ref
22
22
 
23
- IMPORTANT: `config_ref` is deprecated. The <<prop>> macro supersedes all of its functionality: it validates names against the published property reference, discovers the target page dynamically instead of taking a manual path, powers hover tooltips, and its `helm-path=auto` attribute replaces `config_ref`'s Kubernetes prefixing with a per-property correct Helm values path (`config_ref` prefixed every property with `storage.tiered.config.`, mislabeling non-tiered properties). Migrate call sites with `doc-tools generate migrate-property-refs --config-refs`. The macro stays registered only because the generated property partials still emit it.
23
+ IMPORTANT: `config_ref` is deprecated. The `prop` macro supersedes all of its functionality: it validates names against the published property reference, discovers the target page dynamically instead of taking a manual path, powers hover tooltips, and its `helm-path=auto` attribute replaces `config_ref`'s Kubernetes prefixing with a per-property correct Helm values path (`config_ref` prefixed every property with `storage.tiered.config.`, mislabeling non-tiered properties). Migrate call sites with `doc-tools generate migrate-property-refs --config-refs`. The macro stays registered only because older versioned docs, which are frozen, still contain it; generated content emits `prop` (the extractor rewrites any `config_ref` it finds).
24
24
 
25
25
  This inline macro is used to generate a reference to a configuration value in the Redpanda documentation. The macro's parameters allow for control over the generated reference's format and the type of output produced.
26
26
 
@@ -46,7 +46,7 @@ This is the path to the document where the configuration value is defined. This
46
46
 
47
47
  IMPORTANT: The path must be the name of a document at the root of the `reference` module.
48
48
 
49
- NOTE: The `config_ref` macro is environment-aware. It checks if the document it is being used in is part of a Kubernetes environment by checking if the `env-kubernetes` attribute is set in the document's attributes. Depending on this check, it either prepends `storage.tieredConfig.` to the `configRef` or just uses the `configRef` as is.
49
+ NOTE: The `config_ref` macro is environment-aware. It checks if the document it is being used in is part of a Kubernetes environment by checking if the `env-kubernetes` attribute is set in the document's attributes. Depending on this check, it either prepends `storage.tiered.config.` to the `configRef` or just uses the `configRef` as is.
50
50
 
51
51
  For example:
52
52
 
@@ -113,6 +113,31 @@ Set to `false` to render the term without a link.
113
113
  enterprise-beta-badge::
114
114
  Set to `false` to suppress the beta badge in prose. Generated tables always show it. See <<beta-features>>.
115
115
 
116
+ [[release-status]]
117
+ === Release status
118
+
119
+ A registry entry's `status` field declares where a feature may be referenced. It is the enterprise equivalent of `cloud_supported` for properties: one field in the data, checked at build time, rather than a rule reviewers have to remember.
120
+
121
+ [cols="1,3a"]
122
+ |===
123
+ | `status` | Behavior
124
+
125
+ | `ga` (default)
126
+ | Shipped. Referenced anywhere, no badge. Applies when `status` is absent, so existing entries need no change.
127
+
128
+ | `beta`
129
+ | Publicly available as a beta. Referenced anywhere, with a `beta` badge in prose and in the generated tables.
130
+
131
+ | `unreleased`
132
+ | In a release candidate only, not public. Referenced *only* from a prerelease component version. On a released page the mention renders as plain text with no enterprise styling, tooltip, or link, and the build reports it according to `enterprise-validate`. Omitted from `enterprise_features` tables on released pages; listed with its badge on prerelease pages.
133
+ |===
134
+
135
+ A prerelease page is one whose component version sets `prerelease` in `antora.yml`. A playbook can declare it directly with the `page-component-version-is-prerelease` document attribute.
136
+
137
+ `beta: true` predates this field and still means `status: beta`, so old entries keep working. Prefer `status` in new entries, because a boolean cannot express "not public at all". An unrecognized `status` value is reported rather than silently treated as shipped. Use `status-tooltip` to override the `unreleased` badge's hover text.
138
+
139
+ For the writer-facing procedure, see link:DOCUMENTING_UNRELEASED_FEATURES.adoc[Documenting properties and enterprise features before they ship].
140
+
116
141
  [[beta-features]]
117
142
  === Beta features
118
143
 
@@ -159,7 +184,7 @@ asciidoc:
159
184
 
160
185
  == enterprise_features
161
186
 
162
- This block macro renders the licensing feature table for one scope from the enterprise features registry (see <<enterprise>>). Because the table is generated, it can never drift from the registry.
187
+ This block macro renders the licensing feature table for one scope from the enterprise features registry (see the `enterprise` macro above). Because the table is generated, it can never drift from the registry.
163
188
 
164
189
  === Usage
165
190
 
@@ -180,13 +205,13 @@ Optional third-column heading override. The default is `Behavior Upon Expiration
180
205
 
181
206
  === Registration
182
207
 
183
- Registered together with the `enterprise` inline macro (see <<enterprise>>).
208
+ Registered together with the `enterprise` inline macro.
184
209
 
185
210
  == prop
186
211
 
187
212
  This inline macro references a Redpanda configuration property in prose. The property renders as a marked `<code>` element (`property-ref` class plus a `data-property-name` attribute) that the docs UI decorates with a hover-documentation tooltip. Marking is opt-in: only properties referenced through this macro get tooltips, so ambiguous words such as `admin` or `rack` in unrelated contexts (Helm values, feature settings) are never decorated by mistake.
188
213
 
189
- The macro validates its target against the published property reference: the newest `redpanda-properties-<tag>.json` attachment in the `reference` module of the build, the same data the tooltips fetch at runtime. To migrate existing plain-backtick property mentions, use `doc-tools generate migrate-property-refs`.
214
+ The macro checks that a property is actually available to the audience reading the page, and links it only where a page documents it. It validates against the published property data: a `redpanda-properties-<tag>.json` attachment in the `reference` module, the same data the tooltips fetch at runtime. See <<prop-availability>> for which properties each doc set may reference and <<prop-version-selection>> for which file a given page uses. To migrate existing plain-backtick property mentions, use `doc-tools generate migrate-property-refs`.
190
215
 
191
216
  === Usage
192
217
 
@@ -194,20 +219,20 @@ The macro validates its target against the published property reference: the new
194
219
  ----
195
220
  prop:cloud_storage_enabled[]
196
221
  prop:iceberg_enabled[link=true]
197
- prop:retention_ms[link=true,page=properties/topic-properties]
222
+ prop:retention.ms[link=true,page=properties/topic-properties]
198
223
  prop:write_caching_default[text=write caching]
199
224
  ----
200
225
 
201
226
  The macro target is the property name. Named attributes:
202
227
 
203
228
  link::
204
- Set to `true` to also link the property to its reference page. The page is discovered dynamically: the macro indexes which reference-module page in the current component documents each property, by scanning property headings in the partials each page includes (respecting include tag filters such as `tags=redpanda-cloud`). Links are component-relative, so a Cloud page links to the Cloud component's own property pages, and they keep working if properties are split across different pages in the future. When discovery finds nothing, the page falls back deterministically to the property's `config_scope` (`cluster`, `broker`, or `topic`). This supersedes the `config_ref` macro's linking for most uses.
229
+ Set to `true` to also link the property to its reference page. The page is discovered dynamically: the macro indexes which reference-module page in the current component documents each property, by scanning property headings in the partials each page includes (respecting include tag filters such as `tags=redpanda-cloud`). Links are component-relative, so a Cloud page links to the Cloud component's own property pages, and they keep working if properties are split across different pages in the future. See <<prop-availability>> for properties the current component does not document. This supersedes the `config_ref` macro's linking for most uses.
205
230
 
206
231
  page::
207
232
  Optional reference-page override (module-relative, without `.adoc`), for example `properties/object-storage-properties`. Overrides discovery entirely.
208
233
 
209
234
  helm-path::
210
- Set to `auto` to display the property as its Helm values path on pages rendered with the `env-kubernetes` attribute, so single-sourced content reads correctly for both Linux and Kubernetes audiences. The path is derived per property: `storage.tiered.config.<name>` for tiered storage properties, `config.node.<name>` for broker properties, and `config.cluster.<name>` for other cluster properties. On pages without `env-kubernetes`, the plain property name renders. This supersedes the deprecated `config_ref` macro's prefixing.
235
+ Set to `auto` to display the property as its Helm values path on pages rendered with the `env-kubernetes` attribute, so single-sourced content reads correctly for both Linux and Kubernetes audiences. The path is derived per property: `storage.tiered.config.<name>` for properties whose name begins `cloud_storage_`, `config.node.<name>` for broker properties, and `config.cluster.<name>` for cluster properties. Topic properties render as the plain name, because they are set per topic through the Kafka API, rpk, or a Topic resource and have no `values.yaml` key at all. The plain name also renders on any page where `env-kubernetes` is unset or explicitly `false`. This supersedes the deprecated `config_ref` macro's prefixing.
211
236
 
212
237
  text::
213
238
  Optional display text override. Wins over the Helm path display.
@@ -215,11 +240,75 @@ Optional display text override. Wins over the Helm path display.
215
240
  === Configuration options
216
241
 
217
242
  property-validate::
218
- How to report a target that does not match any published property: `warn` (default) logs a build warning with the file path and close-match suggestions, `error` fails the build, and `off` disables validation. When no property JSON is available in the build, the macro renders without validation and logs a single warning.
243
+ How to report a target that does not match any published property: `warn` (default) logs a build warning with the file path, the dataset the page was checked against, and close-match suggestions; `error` fails the build; and `off` disables validation and silences the unknown-name, unavailable-property, unpublished-property and missing-data warnings. Diagnostics about the build itself are not silenced by `off`, because they report a broken input rather than a questionable mention: unusable property JSON, a property claimed by two declared sources, and an index built after conversion started. When no property JSON is available in the build, the macro renders without validation and logs one warning per component version.
244
+
245
+ [[prop-version-selection]]
246
+ === Which property data a page validates against
247
+
248
+ Each published Redpanda release series ships its own `redpanda-properties-v<major>.<minor>.<patch>.json`, so a page must be checked against the data for *its own* series. A 25.3 page validated against 26.2 data would accept properties that release never had and flag removed ones as typos.
249
+
250
+ The macro therefore selects by series (`major.minor`) rather than by newest tag:
251
+
252
+ . For a component version with a numeric series (`streaming` at `25.3`), the newest patch whose tag matches that series -- preferring the copy attached to the page's own version, then the same series published anywhere in the component, since a series' JSON sometimes lives only on the main branch. Patch numbers compare numerically, so `v25.3.11` beats `v25.3.2`.
253
+ . For a Cloud component (`env-cloud`), the newest `streaming` dataset, then `ROOT`. Cloud is managed Redpanda and always tracks the current release, so its own version is never consulted.
254
+
255
+ Selecting by series rather than newest tag matters in practice: the docs repo's main branch carries the previous series' JSON alongside its own, and a prerelease tag could land there too. Newest-tag-wins would hand that file to pages of a different release. A component with no property data and no `env-cloud` attribute gets no dataset at all -- see <<prop-availability>>.
256
+
257
+ Tags compare by release precedence, so a GA release outranks its own release candidates and `rc2` outranks `rc1`. A cycle publishes `v26.3.1-rc1`, then `rc2`, then the GA `v26.3.1`, and a beta branch at `version: 26.3` lands on the newest of those deterministically rather than on whichever file the content catalog happened to yield first. Redpanda spells prereleases as one identifier (`-rc2`, not `-rc.2`), which the semver spec compares as a string, so the trailing digits are split out before comparing; otherwise `rc10` would sort below `rc2`.
258
+
259
+ Cloud takes the newest *GA* dataset and ignores prereleases entirely, because Cloud runs released Redpanda. An RC published on a beta branch outranks the current GA by version number, so without this every Cloud page would start validating against an unreleased property list. When a build contains only prerelease data, Cloud goes unvalidated and the warning says so specifically rather than reporting no data at all.
260
+
261
+ When a component publishes property data for other series but none for the page's own, the macro declines to validate rather than use a neighbouring release's data. It logs one warning per component version naming the missing file, the tags it did find, and the `doc-tools generate property-docs --tag` command that produces it. `prop:` targets on those pages render unvalidated and their tooltips carry no data, so either generate the series' JSON on that branch or leave `prop:` out of that version's content.
219
262
 
220
263
  property-ref-role::
221
264
  CSS class applied to the code element. The default is `property-ref`.
222
265
 
266
+ [[prop-availability]]
267
+ === Which properties a doc set may reference
268
+
269
+ Property data exists for two doc sets.
270
+
271
+ *Self-managed (`streaming`)* publishes its own properties JSON per release, as a reference-module attachment. Every property in that release's file is available.
272
+
273
+ *Cloud* is managed Redpanda, so it has no property data of its own and always tracks the newest self-managed file. A Cloud component is recognized by its `env-cloud` attribute.
274
+
275
+ A *cluster* property is available to Cloud unless its `cloud_supported` field is explicitly `false`. That field is the union of the Cloud states -- no published property is `cloud_editable` or `cloud_readonly` without also being `cloud_supported` -- so it is the single gate for that scope.
276
+
277
+ *Topic and broker* properties are not gated by it. `cloud_supported` is derived from the Cloud control plane's install pack, whose two sections (`customer_managed_configs` and `readonly_cluster_config`) describe cluster configuration and contain nothing else: every entry resolves to a cluster property, and no topic property appears anywhere in the file. A `false` value on another scope therefore asserted something that source cannot know, while Cloud users set topic properties routinely through the Kafka API and Console and Cloud publishes a topic properties reference of its own. The property extractor no longer writes the field for other scopes, and the macro treats an absent value as "no opinion", so already-published datasets stay correct too.
278
+
279
+ Any other component (`connect`, the agentic data plane, a preview site) has no property data, and none is inferred for it. Those components carry their own product versions, which say nothing about which Redpanda properties exist, so a version number is never read as a Redpanda release. Supporting a further doc set is a deliberate addition to the macro.
280
+
281
+ When a property is not available to the page's audience -- a non-`cloud_supported` property on a Cloud page -- the macro renders it as plain `<code>`: no link, no `data-property-name` marker, and therefore no tooltip. A tooltip would imply the reader can set something they cannot. The build logs a warning naming the property, the file, and the dataset consulted. Fix `cloud_supported` in the property extractor if the data is wrong; otherwise reword the sentence and drop the macro.
282
+
283
+ When no dataset covers the component at all, the macro warns once per component version and renders every `prop:` target as plain code, rather than emitting tooltips it could not verify.
284
+
285
+ === Linking
286
+
287
+ `link=true` links the property to its reference page, but only when a page *in the same component* documents it. Nothing is linked across components, and no target is guessed from the property's `config_scope`: a link the build cannot verify is a broken link. An available property that no page renders -- a deprecated property, or one held back by include tags -- keeps its tooltip and renders unlinked; `link=true` warns in that case. Use `page=` to set a target explicitly for one mention.
288
+
289
+ [[prop-property-source]]
290
+ ==== Declaring which page is the reference
291
+
292
+ A reference page can declare the property collection it publishes:
293
+
294
+ [,asciidoc]
295
+ ----
296
+ = Cluster Configuration Properties
297
+ :page-property-source: cluster-properties
298
+ ----
299
+
300
+ Claims on a property are then ranked, highest first:
301
+
302
+ . A page that declares `page-property-source`.
303
+ . A property heading pulled in from a generated partial.
304
+ . A bare heading on a page that happens to match a property name.
305
+
306
+ The ranking exists because inference alone is ambiguous. Several real property names are ordinary words -- `admin`, `brokers`, `rack`, `retries`, `superusers` -- so any reference page titled or sectioned with one of those would otherwise claim the property from the page that documents it, and a page title generates no anchor, so both the link and the tooltip URL would point at nothing. Ranking also makes resolution independent of the order Antora aggregates content, which is stable for one checkout but carries no meaning.
307
+
308
+ Within a rank, the first page wins, ordered by path so the result is deterministic. When two *declared* pages claim the same property the build warns and names both: that is an authoring conflict rather than something to resolve quietly.
309
+
310
+ Declaring the attribute is optional. Existing pages keep working through inference, and the declaration is worth adding when a property could plausibly be claimed by more than one page in the component, or to state the intent explicitly rather than relying on the generated partials happening to win.
311
+
223
312
  === Registration
224
313
 
225
314
  [,yaml]
package/macros/badge.js CHANGED
@@ -15,22 +15,52 @@
15
15
  * @param {string} [opts.tooltip] - Tooltip text, rendered as `data-tooltip`.
16
16
  * @returns {string} Badge HTML.
17
17
  */
18
+ // Hover text for the labels this macro is asked for by name. The badge is
19
+ // styled with a help cursor, so a badge with nothing to say promises an
20
+ // explanation and then withholds it. Callers with something more specific pass
21
+ // their own tooltip, which wins.
22
+ const DEFAULT_TOOLTIPS = {
23
+ beta: 'This feature is in public beta. You can enable it, but it may change before it is generally available.',
24
+ unreleased: 'This feature is not in a released version of Redpanda yet. It is documented here for the upcoming release.',
25
+ };
26
+
18
27
  function buildBadgeHtml ({ label, size, tooltip } = {}) {
19
28
  const text = label || 'label';
20
- const className = `badge--${text.toLowerCase().replace(/\s+/g, '-')}`;
21
29
  const isLarge = size === 'large';
22
30
  const sizeClass = isLarge ? 'badge--large' : '';
23
- const tooltipAttr = tooltip ? ` data-tooltip="${tooltip}"` : '';
31
+ // Escape before interpolating: these values reach the macro from .adoc source
32
+ // and from registry fields, and an unescaped quote terminates the attribute
33
+ // early, turning the rest of the text into stray attributes (including event
34
+ // handlers). The label needs it as much as the tooltip -- it lands in the
35
+ // class attribute and in the text content, and escaping only the tooltip left
36
+ // `badge:x[label=x" onmouseover="...]` free to break out of the class.
37
+ const escapeAttr = (value) => String(value)
38
+ .replace(/&/g, '&amp;')
39
+ .replace(/"/g, '&quot;')
40
+ .replace(/</g, '&lt;')
41
+ .replace(/>/g, '&gt;');
42
+ const className = `badge--${escapeAttr(text.toLowerCase().replace(/\s+/g, '-'))}`;
43
+ // hasOwnProperty, not a bare index: `label=constructor` and `label=__proto__`
44
+ // are already lowercase, so a bare lookup shipped
45
+ // `function Object() { [native code] }` to readers as hover text. prop.js
46
+ // guards its property lookups the same way.
47
+ const labelKey = text.trim().toLowerCase();
48
+ const defaultTooltip = Object.prototype.hasOwnProperty.call(DEFAULT_TOOLTIPS, labelKey)
49
+ ? DEFAULT_TOOLTIPS[labelKey]
50
+ : undefined;
51
+ const hoverText = tooltip || defaultTooltip;
52
+ const tooltipAttr = hoverText ? ` data-tooltip="${escapeAttr(hoverText)}"` : '';
24
53
 
25
54
  // Add brackets if not large. The brackets matter beyond styling: they are
26
55
  // real text content, so the label survives an HTML-to-Markdown conversion as
27
56
  // "(beta)" rather than vanishing with the CSS class.
28
- const renderedLabel = isLarge ? text : `(${text})`;
57
+ const renderedLabel = isLarge ? escapeAttr(text) : `(${escapeAttr(text)})`;
29
58
 
30
59
  return `<span class="badge ${className} ${sizeClass}"${tooltipAttr}>${renderedLabel}</span>`;
31
60
  }
32
61
 
33
62
  module.exports.buildBadgeHtml = buildBadgeHtml;
63
+ module.exports.DEFAULT_TOOLTIPS = DEFAULT_TOOLTIPS;
34
64
 
35
65
  module.exports.register = function (registry) {
36
66
  registry.inlineMacro(function () {