@redpanda-data/docs-extensions-and-macros 5.38.8 → 5.38.10

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.
@@ -261,11 +261,26 @@ function anchorIndex (contentCatalog, attachment, properties, logger) {
261
261
  }
262
262
  for (const [name, entry] of pages) {
263
263
  if (!entry || !entry.page) continue
264
- index.set(propertyAnchor(name), { page: entry.page, name })
264
+ const anchor = propertyAnchor(name)
265
+ index.set(anchor, { page: entry.page, name, anchor })
266
+ // Legacy spelling. Before anchors replaced dots with hyphens they dropped
267
+ // them, so descriptions written then say <<redpandaremoteread>> for
268
+ // redpanda.remote.read, and some carry a stray hyphen (<<redpandastorage-mode>>).
269
+ // Those still name a documented property; only the spelling is old. Index
270
+ // the dot-and-hyphen-free form too, so they resolve to the real anchor
271
+ // instead of degrading to plain text and a warning on every build of every
272
+ // version still carrying them. A real anchor always wins over a legacy one.
273
+ const legacy = LEGACY_ANCHOR_PREFIX + legacyAnchorKey(name)
274
+ if (!index.has(legacy)) index.set(legacy, { page: entry.page, name, anchor })
265
275
  }
266
276
  return index
267
277
  }
268
278
 
279
+ const LEGACY_ANCHOR_PREFIX = 'legacy:'
280
+ function legacyAnchorKey (name) {
281
+ return name.replace(/[.-]/g, '')
282
+ }
283
+
269
284
  /**
270
285
  * Rewrite <<anchor>> to an xref before conversion.
271
286
  *
@@ -278,8 +293,8 @@ function anchorIndex (contentCatalog, attachment, properties, logger) {
278
293
  function resolveInternalRefs (description, anchors, report) {
279
294
  return description.replace(INTERNAL_REF_RX, (match, anchor, display) => {
280
295
  const text = (display || '').trim()
281
- const target = anchors.get(anchor)
282
- if (target) return `xref:reference:${target.page}.adoc#${anchor}[${attrlistValue(text || `\`${target.name}\``)}]`
296
+ const target = anchors.get(anchor) || anchors.get(LEGACY_ANCHOR_PREFIX + legacyAnchorKey(anchor))
297
+ if (target) return `xref:reference:${target.page}.adoc#${target.anchor || anchor}[${attrlistValue(text || `\`${target.name}\``)}]`
283
298
  // The anchor names no documented property, so there is nothing to link to.
284
299
  // Asciidoctor would render a same-page fragment, and a tooltip is shown on
285
300
  // arbitrary pages, so that link would go nowhere and still invite a click --
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@redpanda-data/docs-extensions-and-macros",
3
- "version": "5.38.8",
3
+ "version": "5.38.10",
4
4
  "description": "Antora extensions and macros developed for Redpanda documentation.",
5
5
  "keywords": [
6
6
  "antora",
@@ -207,15 +207,21 @@ async function handleMergeUnavailable(tag, enhanced, reason, output = enhanced)
207
207
  }
208
208
 
209
209
  // rp_util covers every cluster/broker-scope property (see this file's own
210
- // header comment), so when its merge is skipped or fails, any cluster/
211
- // broker property still missing gets_restored isn't "no annotation exists"
212
- // (the pre-existing, legitimate case property.hbs already renders as an
213
- // absent row) -- it's "we don't know, because the merge that would have
214
- // told us didn't run". Mark those explicitly so the template can render
215
- // that as a visible "Unknown" state instead of silently rendering nothing,
216
- // which previously made a flaky rp_util fetch indistinguishable from a
217
- // real upstream change in the generated docs.
210
+ // header comment), so when its merge is skipped or fails, a cluster/broker
211
+ // property still missing gets_restored means "we don't know, because the
212
+ // merge that would have told us didn't run". Mark those so the template can
213
+ // render a visible "Unknown" instead of silently rendering nothing, which
214
+ // made a flaky rp_util fetch indistinguishable from a real upstream change.
215
+ //
216
+ // Since the Tree-sitter pass materializes the declared default (see
217
+ // GetsRestoredTransformer), a source-parsed property always arrives with the
218
+ // key set, so this now marks only entries with no source to default from.
219
+ // That is the honest scope for it: an override-fabricated stub has no
220
+ // declaration to read, but the reason is the missing property, not the
221
+ // missing merge, so those are skipped too and simply render no row.
218
222
  const RP_UTIL_COVERED_SCOPES = new Set(['cluster', 'broker'])
223
+ const NO_SOURCE_PROVENANCE = 'override'
224
+
219
225
 
220
226
  function markRpUtilMergeUnavailable(enhancedPath) {
221
227
  let data
@@ -230,12 +236,17 @@ function markRpUtilMergeUnavailable(enhancedPath) {
230
236
  for (const prop of Object.values(properties)) {
231
237
  if (!prop || !RP_UTIL_COVERED_SCOPES.has(prop.config_scope)) continue
232
238
  if (prop.gets_restored !== undefined) continue
239
+ if (prop.defined_in === NO_SOURCE_PROVENANCE) continue
233
240
  prop.rp_util_merge_status = 'unavailable'
234
241
  marked++
235
242
  }
236
243
  if (marked === 0) return
237
244
  try {
238
- fs.writeFileSync(enhancedPath, bigIntJson.stringify(data))
245
+ // indent 4, matching what property_extractor.py and rp_util_merge.py
246
+ // write. Without it this rewrite minified the whole dataset onto one
247
+ // line, flipping the committed attachment from 14,000 diffable lines
248
+ // to one.
249
+ fs.writeFileSync(enhancedPath, bigIntJson.stringify(data, 4))
239
250
  } catch (err) {
240
251
  console.warn(`Warning: could not write rp_util-unavailable marker to ${enhancedPath}: ${err.message}`)
241
252
  }
@@ -155,17 +155,19 @@ endif::[]
155
155
  {{/if}}
156
156
  {{/if}}
157
157
 
158
- ifndef::env-cloud[]
159
158
  {{#if (ne gets_restored undefined)}}
160
159
 
160
+ ifndef::env-cloud[]
161
161
  | Restored on xref:manage:whole-cluster-restore.adoc[Whole Cluster Restore]
162
162
  | {{#if gets_restored}}Yes{{else}}No{{/if}}
163
+ endif::[]
163
164
  {{else if (eq rp_util_merge_status "unavailable")}}
164
165
 
166
+ ifndef::env-cloud[]
165
167
  | Restored on xref:manage:whole-cluster-restore.adoc[Whole Cluster Restore]
166
168
  | Unknown (rp_util merge unavailable this build)
167
- {{/if}}
168
169
  endif::[]
170
+ {{/if}}
169
171
  {{#if visibility}}
170
172
 
171
173
  ifndef::env-cloud[]
@@ -132,13 +132,13 @@ endif::[]
132
132
  | Nullable
133
133
  | {{#if nullable}}Yes{{else}}No{{/if}}
134
134
 
135
- ifndef::env-cloud[]
136
135
  {{#if (ne gets_restored undefined)}}
137
136
 
137
+ ifndef::env-cloud[]
138
138
  | Restored on xref:manage:whole-cluster-restore.adoc[Whole Cluster Restore]
139
139
  | {{#if gets_restored}}Yes{{else}}No{{/if}}
140
- {{/if}}
141
140
  endif::[]
141
+ {{/if}}
142
142
  {{#if example}}
143
143
 
144
144
  | Example
@@ -119,6 +119,16 @@ class TopicPropertyExtractor:
119
119
  "default": None, # Will be populated from cluster property if available
120
120
  "is_noop": False, # Will be updated later in _correlate_properties_with_data
121
121
  "is_topic_property": True,
122
+ # A topic property is a Kafka topic config, not a
123
+ # config::property, so there is no per-property
124
+ # `restored` annotation to read and rp_util's dumps
125
+ # do not cover topic scope. Whole Cluster Restore
126
+ # restores topic metadata (see the WCR docs), which
127
+ # is where these live, so the answer is yes for all
128
+ # of them. Set it explicitly: topic-property.hbs
129
+ # drops the row when the key is absent, which
130
+ # silently deleted it from all 48 properties.
131
+ "gets_restored": True,
122
132
  "config_scope": "topic"
123
133
  }
124
134
  print(f"Found {total_matches} topic properties in {file_path}")
@@ -176,6 +186,16 @@ class TopicPropertyExtractor:
176
186
  "default": None, # Will be populated from cluster property if available
177
187
  "is_noop": False, # Will be updated later in _correlate_properties_with_data
178
188
  "is_topic_property": True,
189
+ # A topic property is a Kafka topic config, not a
190
+ # config::property, so there is no per-property
191
+ # `restored` annotation to read and rp_util's dumps
192
+ # do not cover topic scope. Whole Cluster Restore
193
+ # restores topic metadata (see the WCR docs), which
194
+ # is where these live, so the answer is yes for all
195
+ # of them. Set it explicitly: topic-property.hbs
196
+ # drops the row when the key is absent, which
197
+ # silently deleted it from all 48 properties.
198
+ "gets_restored": True,
179
199
  "config_scope": "topic"
180
200
  }
181
201
  except Exception as e:
@@ -479,7 +479,15 @@ def find_meta_dict(info):
479
479
 
480
480
  # Case 1: Already parsed dict
481
481
  if isinstance(val, dict) and any(
482
- k in val for k in ("needs_restart", "visibility", "deprecated", "secret", "experimental")
482
+ k in val
483
+ for k in (
484
+ "needs_restart", "visibility", "deprecated", "secret", "experimental",
485
+ # A block holding only one of these is still a meta block. Left
486
+ # out, `meta{ .gets_restored = gets_restored::no }` resolved to
487
+ # None here and the caller fell back to its default, inverting
488
+ # the value it had already parsed correctly.
489
+ "gets_restored", "restored",
490
+ )
483
491
  ):
484
492
  return val
485
493
 
@@ -1072,14 +1080,45 @@ class GetsRestoredTransformer:
1072
1080
  """
1073
1081
 
1074
1082
  def accepts(self, info, file_pair):
1075
- """Process properties with backup/restore metadata."""
1076
- return (get_meta_value(info, "gets_restored") is not None or
1077
- get_meta_value(info, "restored") is not None)
1083
+ """Process every property parsed from source, annotated or not.
1084
+
1085
+ Accepting only annotated properties left the key absent for the
1086
+ majority, and absent then had to carry two incompatible meanings:
1087
+ "the declared default applies" and "the data never reached us". The
1088
+ templates cannot tell those apart, and for a release whose rp_util
1089
+ schema can never be published they picked the second, rendering
1090
+ "Unknown" for 626 properties whose value is not in doubt.
1091
+
1092
+ A property reaching this transformer was parsed out of the C++, so the
1093
+ first meaning is the true one and this is the place that knows it.
1094
+ """
1095
+ return True
1078
1096
 
1079
1097
  def parse(self, property, info, file_pair):
1080
- """Extract restoration flag from either naming convention."""
1081
- val = get_meta_value(info, "gets_restored") or get_meta_value(info, "restored", "no")
1082
- property["gets_restored"] = (val != "no")
1098
+ """Extract the restoration flag, defaulting to the declared default.
1099
+
1100
+ base_property.h declares `gets_restored gets_restored{gets_restored::yes}`,
1101
+ so a property carrying no annotation is restored. Materialize that
1102
+ rather than leaving it implicit: at v26.2.2 every one of the 17
1103
+ annotated properties says `no`, so the default is what the other 674
1104
+ actually rely on.
1105
+
1106
+ rp_util's runtime value still wins wherever the merge runs -- it sets
1107
+ the same key afterwards (see rp_util_merge.map_rp_util_property) -- so
1108
+ this is the fallback for the releases the merge cannot cover, not a
1109
+ competing source of truth.
1110
+ """
1111
+ val = get_meta_value(info, "gets_restored")
1112
+ if val is None:
1113
+ val = get_meta_value(info, "restored")
1114
+ if val is not None:
1115
+ property["gets_restored"] = (val != "no")
1116
+ else:
1117
+ # MetaParamTransformer runs first (property_extractor.py) and sets
1118
+ # this key off the same meta block, so only default when nothing
1119
+ # upstream resolved a real value. Assigning unconditionally would
1120
+ # overwrite an explicit `no` with the default.
1121
+ property.setdefault("gets_restored", True)
1083
1122
  return property
1084
1123
 
1085
1124