@redpanda-data/docs-extensions-and-macros 5.41.0 → 5.44.0

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.
package/bin/doc-tools.js CHANGED
@@ -1476,6 +1476,9 @@ automation
1476
1476
  * parsing `-X list` text for rpk versions that predate it. Hidden -X options
1477
1477
  * appear in neither source, so they are excluded automatically.
1478
1478
  *
1479
+ * The table is sectioned by the API group rpk reports for each option, and is
1480
+ * a single flat table on rpk versions that report no groups.
1481
+ *
1479
1482
  * The main rpk-docs pipeline also writes this partial from the tree it
1480
1483
  * already holds; this standalone command is for targeted refreshes without
1481
1484
  * a full generation run.
@@ -1492,7 +1495,7 @@ automation
1492
1495
  */
1493
1496
  automation
1494
1497
  .command('rpk-env-partial')
1495
- .description('Generate the -X -> RPK_* env var mapping partial from rpk -X list output.')
1498
+ .description('Generate the -X -> RPK_* env var mapping partial from rpk itself.')
1496
1499
  .option('-r, --ref <ref>', 'Git branch or tag to build rpk from (e.g., dev, v26.2.1). Clones from GitHub.')
1497
1500
  .option('--from-source <path>', 'Path to local rpk source (src/go/rpk directory)')
1498
1501
  .option('--rpk-bin <path>', 'Path to an existing rpk binary (skips clone and build)')
@@ -47,16 +47,16 @@ The build validates the name against the RC data. A typo gets a warning naming t
47
47
 
48
48
  === If you forget the property data
49
49
 
50
- You get told, once per component version, with the command to run:
50
+ You get told, once per component version, with the command to run. Every excerpt below is what actually lands in the build log, through Antora's own logger rather than the macro printing its own text -- `HH:MM:SS.mmm` stands in for the real timestamp, which varies on every build:
51
51
 
52
52
  [literal]
53
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.
54
+ [HH:MM:SS.mmm] WARN (prop-macro): prop macro: streaming@26.3 publishes no
55
+ redpanda-properties-v26.3.*.json attachment in its reference module (found for
56
+ other series: v26.2.1). prop: targets on 26.3 pages are therefore not validated
57
+ and their tooltips carry no data, rather than being checked against another
58
+ release's properties. Generate it with 'npx doc-tools generate property-docs
59
+ --tag v26.3.<patch>' on that branch, or leave prop: out of 26.3 content.
60
60
  ----
61
61
 
62
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.
@@ -116,6 +116,34 @@ Enterprise features are declared in the registry (`enterprise-features.yml` in t
116
116
 
117
117
  Add `status-tooltip` to override the hover text on an `unreleased` badge, or `beta-tooltip` on a `beta` badge.
118
118
 
119
+ === A shipped feature that only exists as of a version: `since`
120
+
121
+ `status` answers "is this public yet," which is a question about a *branch* (prerelease or not). A different question comes up once a feature has fully shipped: an ordinarily-GA feature -- no `status` key at all -- still has a *first* version, and older released versions predate it. Without a check, a feature that ships in 26.3 would render as fully available on a 24.1 page that predates it by two years, because `status` alone has nothing to say about that.
122
+
123
+ `since` closes that gap:
124
+
125
+ [,yaml]
126
+ ----
127
+ - name: My Shipped Feature
128
+ scope: redpanda
129
+ since: '26.3'
130
+ description: What it does.
131
+ expiration: What happens when the licence expires.
132
+ ----
133
+
134
+ The check is against the *page's own version*, not the newest release: a mention on a 26.3 (or later) page renders normally, with no badge, because as of that page's version the feature has shipped. The identical mention on a 26.2 page renders as plain text with no styling, tooltip, or link, and a warning -- the same failure mode as an unreleased feature, but for a version reason rather than a branch reason:
135
+
136
+ [literal]
137
+ ----
138
+ [HH:MM:SS.mmm] WARN (enterprise-macro): enterprise:My Shipped Feature[] in
139
+ modules/manage/pages/example.adoc: 'My Shipped Feature' is marked since: 26.3 in
140
+ the enterprise features registry, but this page is version 26.2, which predates
141
+ it. Move the mention to a page whose version is at or after the since version, or
142
+ remove since if the feature covers this version too.
143
+ ----
144
+
145
+ `since` and `status: unreleased` are independent and can combine: a feature still in an RC carries `status: unreleased` while it is not public at all, then drops that key once it ships and (optionally) gains `since` set to the version it shipped in, so older released versions stop claiming it. Most shipped features need neither key.
146
+
119
147
  === Writing the mention
120
148
 
121
149
  Unchanged:
@@ -129,12 +157,12 @@ On the prerelease branch this renders with the `unreleased` badge. On released d
129
157
 
130
158
  [literal]
131
159
  ----
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.
160
+ [HH:MM:SS.mmm] WARN (enterprise-macro): enterprise:My Upcoming Feature[] in
161
+ modules/manage/pages/example.adoc: 'My Upcoming Feature' is marked status:
162
+ unreleased in the enterprise features registry, so it is only documented for an
163
+ upcoming release. This page belongs to a released version, so the mention renders
164
+ as plain text with no enterprise styling, tooltip, or link. Move the mention to
165
+ the prerelease (beta) branch, or change the status once the feature ships.
138
166
  ----
139
167
 
140
168
  The `unreleased` badge cannot be suppressed with `enterprise-beta-badge=false`. "Not in a release yet" is information a reader needs, not decoration.
@@ -167,19 +195,22 @@ Before opening a PR that documents something unreleased:
167
195
  * [ ] The mention is on the prerelease branch, not on `main` or a released `v/*` branch.
168
196
  * [ ] For a property: the RC property JSON is committed on that branch, generated from the RC tag.
169
197
  * [ ] For an enterprise feature: the registry entry has `status: unreleased`, not `beta: true`, unless it is genuinely a public beta.
198
+ * [ ] For a shipped feature gated to a specific version: the registry entry has `since` set to the version it ships in, not left unset.
170
199
  * [ ] The build log has no `prop macro:` or `enterprise:` warnings for your additions.
171
200
  * [ ] No mention of an unreleased property or feature on a Cloud page.
172
201
 
173
202
  == Seeing it work
174
203
 
175
- The preview site in this repository has fixtures for every case above, including a prerelease component version and a Cloud component:
204
+ The preview site in this repository has fixtures for every case above, including a prerelease component version and a Cloud component. The fast, fully local way to build just those fixtures, with no GitHub token and no real docs/cloud-docs content:
176
205
 
177
206
  [,bash]
178
207
  ----
179
- ./scripts/macro-test.sh --serve
208
+ ./scripts/macro-showcase.sh --serve
180
209
  ----
181
210
 
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.
211
+ That builds `local-macro-showcase-playbook.yml` in seconds. Run `./scripts/macro-test.sh --serve` instead when you also want the fixtures proven against real production content (needs a token; pulls the full docs, cloud-docs, redpanda-labs, and rp-connect-docs repos).
212
+
213
+ Either way, open `/preview/macros-course/` first -- it's a hands-on course, not just a reference: it has you mark a property and register an enterprise feature, then edit the actual fixture data (a property JSON, the enterprise registry) yourself and rebuild to watch the macros react, including reproducing the `status: unreleased` gate on purpose. It links to `/preview/macros-showcase/`, which shows every other macro call, what it renders, and why, as pure reference. `/macro-fixtures/26.2/` and `/macro-fixtures/26.3/` are two versions of the SAME component -- one released, one prerelease (`prerelease: true` in its `antora.yml`) -- so flipping the version selector between them shows the same enterprise: mentions rendering differently with no edits: the unreleased feature badged on 26.3 and gated on 26.2, the since-gated feature (shipped `since: '26.3'`) gated on 26.2 and shipped plain on 26.3, and the beta feature badged on both. `/preview/release-status-test/` shows the unreleased/beta contrast again from a third, versionless component. `/macro-fixtures-cloud/` shows the Cloud availability rules.
183
214
 
184
215
  == Why the build enforces this rather than trusting reviewers
185
216
 
@@ -68,7 +68,7 @@ asciidoc:
68
68
 
69
69
  This inline macro marks a feature as an enterprise feature in prose. The feature name renders as a uniquely styled term with a tooltip explaining that the feature requires an Enterprise Edition license, and links to the feature's documentation or to the licensing page.
70
70
 
71
- The macro validates its target against the enterprise features registry: `modules/ROOT/partials/enterprise-features.yml` in the `shared` component (the `shared/` folder of the docs repository). The registry is the canonical list of enterprise features and their approved external names. Targets are matched case-insensitively, including aliases, and the canonical name, feature page link, and tooltip come from the matching entry. A target that matches no entry is reported according to `enterprise-validate`. To mark a new feature as enterprise, add it to the registry first.
71
+ The macro validates its target against the enterprise features registry: `modules/ROOT/partials/enterprise-features.yml` in the `shared` component (the `shared/` folder of the docs repository). The registry is the canonical list of enterprise features and their approved external names. Targets are matched case-insensitively, including aliases, and the canonical name, feature page link, and tooltip come from the matching entry. A target that matches no entry is reported according to `enterprise-validate` and renders as plain text -- no styling, tooltip, or link, since there is nothing verified to style it with. To mark a new feature as enterprise, add it to the registry first.
72
72
 
73
73
  === Usage
74
74
 
@@ -639,22 +639,27 @@ function enterpriseInlineMacro (config) {
639
639
  let registry
640
640
  if (config && config.contentCatalog) {
641
641
  registry = loadRegistry(config)
642
- if (!registry) warnNoRegistry(config && config.contentCatalog)
642
+ if (!registry) warnNoRegistry(config.contentCatalog)
643
+ }
644
+ if (!registry) {
645
+ // Nothing to validate against, so nothing should render as if it
646
+ // were. Mirrors prop:'s plain fallback for the same reason: a styled
647
+ // term this build cannot check is worse than an unstyled one.
648
+ return self.createInline(parent, 'quoted', attributes.text || target)
643
649
  }
644
650
  let feature = target
645
- let entry
646
- if (registry) {
647
- entry = registry.lookup.get(target.trim().toLowerCase())
648
- if (entry) {
649
- feature = entry.name
650
- } else {
651
- reportUnknownFeature({
652
- feature: target,
653
- mode,
654
- registry,
655
- filePath,
656
- })
657
- }
651
+ const entry = registry.lookup.get(target.trim().toLowerCase())
652
+ if (entry) {
653
+ feature = entry.name
654
+ } else {
655
+ reportUnknownFeature({ feature: target, mode, registry, filePath })
656
+ // An unrecognized name has nothing behind it to style -- no xref, no
657
+ // tooltip, no licence terms to point at. Falling through to the same
658
+ // styled output a real feature gets, just with a generic fallback
659
+ // tooltip, let a typo pass for a real feature with nothing on the
660
+ // page to show a reader it was wrong. Mirrors prop:'s plain fallback
661
+ // for an unrecognized property name, for the same reason.
662
+ return self.createInline(parent, 'quoted', attributes.text || feature)
658
663
  }
659
664
  const status = entryStatus(entry, { mode, filePath, contentCatalog: config && config.contentCatalog })
660
665
  if (status === STATUS_UNRELEASED && !isPrereleasePage(config, document)) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@redpanda-data/docs-extensions-and-macros",
3
- "version": "5.41.0",
3
+ "version": "5.44.0",
4
4
  "description": "Antora extensions and macros developed for Redpanda documentation.",
5
5
  "keywords": [
6
6
  "antora",
@@ -3227,7 +3227,11 @@ function writeEnvVarsPartial (tree, partialsDir) {
3227
3227
  return { written: false }
3228
3228
  }
3229
3229
  const { renderPartial, keyToEnvVar } = require('./generate-x-env-partial.js')
3230
- const options = tree.x_options.map(o => ({ name: o.name, env: o.env || keyToEnvVar(o.name) }))
3230
+ const options = tree.x_options.map(o => ({
3231
+ name: o.name,
3232
+ env: o.env || keyToEnvVar(o.name),
3233
+ groupTitle: o.group_title || null
3234
+ }))
3231
3235
  const output = path.join(partialsDir, 'rpk-env-vars.adoc')
3232
3236
  fs.mkdirSync(path.dirname(output), { recursive: true })
3233
3237
  fs.writeFileSync(output, renderPartial(options))
@@ -11,6 +11,12 @@
11
11
  * descriptions.
12
12
  * 2. `rpk -X list` text output, for rpk versions that predate x_options.
13
13
  *
14
+ * Each x_option also carries the API it configures, as `group` (a stable
15
+ * slug) and `group_title` (the heading), added in
16
+ * redpanda-data/streaming-enterprise#311. When rpk supplies them the table is
17
+ * sectioned by API, so the grouping comes from rpk rather than being restated
18
+ * here; rpk versions without them get one flat table.
19
+ *
14
20
  * The partial is included by both reference:rpk/rpk-x-options.adoc and
15
21
  * reference:environment-variables.adoc in the docs repo. Hidden -X options
16
22
  * (for example cloud_environment, whose values are deliberately
@@ -38,10 +44,12 @@ const GENERATED_BANNER = `// tag::generated[]
38
44
  /**
39
45
  * Extract -X options from `rpk --print-tree` JSON.
40
46
  * @param {string} output - Raw stdout from `rpk --print-tree`
41
- * @returns {Array<{name: string, env: string}>|null} options in display
42
- * order, or null when the tree has no x_options (rpk predates
43
- * redpanda#31520). The env name comes from rpk itself when present
47
+ * @returns {Array<{name: string, env: string, group: ?string, groupTitle: ?string}>|null}
48
+ * options in display order, or null when the tree has no x_options (rpk
49
+ * predates redpanda#31520). The env name comes from rpk itself when present
44
50
  * (the tree's env field is produced by the same derivation rpk reads).
51
+ * group and groupTitle are null on rpk versions that do not group the
52
+ * options.
45
53
  */
46
54
  function xOptionsFromTree (output) {
47
55
  let tree
@@ -51,7 +59,12 @@ function xOptionsFromTree (output) {
51
59
  return null
52
60
  }
53
61
  if (!Array.isArray(tree.x_options) || tree.x_options.length === 0) return null
54
- return tree.x_options.map(o => ({ name: o.name, env: o.env || keyToEnvVar(o.name) }))
62
+ return tree.x_options.map(o => ({
63
+ name: o.name,
64
+ env: o.env || keyToEnvVar(o.name),
65
+ group: o.group || null,
66
+ groupTitle: o.group_title || null
67
+ }))
55
68
  }
56
69
 
57
70
  /**
@@ -96,16 +109,53 @@ function keyToAnchor (key) {
96
109
  return key.replace(/\./g, '-')
97
110
  }
98
111
 
112
+ /**
113
+ * Group options by the API group rpk reports, in first-seen order.
114
+ *
115
+ * Returns null unless every option carries a group title, so a table is
116
+ * either fully sectioned or not sectioned at all: a half-sectioned table
117
+ * would read as though the options with no heading belonged to the section
118
+ * above them.
119
+ *
120
+ * Grouping by title rather than slicing on the boundaries means a group whose
121
+ * options are not contiguous still gets one heading instead of two.
122
+ * @param {Array<{name: string, env: string, groupTitle: ?string}>} options
123
+ * @returns {Array<{title: string, options: Array<Object>}>|null}
124
+ */
125
+ function groupOptions (options) {
126
+ if (!options.every(o => o.groupTitle)) return null
127
+ const byTitle = new Map()
128
+ for (const option of options) {
129
+ const existing = byTitle.get(option.groupTitle)
130
+ if (existing) existing.push(option)
131
+ else byTitle.set(option.groupTitle, [option])
132
+ }
133
+ return [...byTitle].map(([title, opts]) => ({ title, options: opts }))
134
+ }
135
+
136
+ /**
137
+ * Render one table row for an option.
138
+ * @param {{name: string, env: string}} option
139
+ * @returns {string}
140
+ */
141
+ function renderRow ({ name, env }) {
142
+ return `|xref:reference:rpk/rpk-x-options.adoc#${keyToAnchor(name)}[${name}] |${env}`
143
+ }
144
+
99
145
  /**
100
146
  * Render the partial's AsciiDoc content.
101
- * @param {Array<{name: string, env: string}>} options - -X options
147
+ *
148
+ * The table is sectioned by API group when rpk reports one for every option,
149
+ * and is a single flat table otherwise (rpk versions that predate the groups,
150
+ * and the -X list fallback, which has no group data to parse).
151
+ * @param {Array<{name: string, env: string, groupTitle: ?string}>} options - -X options
102
152
  * @returns {string}
103
153
  */
104
154
  function renderPartial (options) {
105
- const rows = options.map(({ name, env }) => {
106
- const xrefTarget = `xref:reference:rpk/rpk-x-options.adoc#${keyToAnchor(name)}[${name}]`
107
- return `|${xrefTarget} |${env}`
108
- })
155
+ const groups = groupOptions(options)
156
+ const body = groups
157
+ ? groups.map(g => `2+s|${g.title}\n${g.options.map(renderRow).join('\n')}`).join('\n\n')
158
+ : options.map(renderRow).join('\n')
109
159
  return `${GENERATED_BANNER}
110
160
  Every \`-X\` option has a corresponding \`RPK_*\` environment variable. Convert by prefixing with \`RPK_\` and replacing dots with underscores:
111
161
 
@@ -113,7 +163,7 @@ Every \`-X\` option has a corresponding \`RPK_*\` environment variable. Convert
113
163
  |===
114
164
  |\`-X\` Option |Environment Variable
115
165
 
116
- ${rows.join('\n')}
166
+ ${body}
117
167
  |===
118
168
  // end::generated[]
119
169
  `
@@ -276,6 +326,7 @@ function handleXEnvPartialGeneration (options) {
276
326
 
277
327
  module.exports = {
278
328
  xOptionsFromTree,
329
+ groupOptions,
279
330
  parseXList,
280
331
  keyToEnvVar,
281
332
  keyToAnchor,