@redpanda-data/docs-extensions-and-macros 4.3.0 → 4.4.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.
Files changed (44) hide show
  1. package/bin/doc-tools.js +328 -0
  2. package/cli-utils/add-caret-external-links.py +68 -0
  3. package/cli-utils/beta-from-antora.js +27 -0
  4. package/cli-utils/generate-cluster-docs.sh +83 -0
  5. package/cli-utils/install-test-dependencies.sh +158 -0
  6. package/cli-utils/python-venv.sh +20 -0
  7. package/cli-utils/start-cluster.sh +53 -0
  8. package/docker-compose/bootstrap.yml +67 -0
  9. package/docker-compose/docker-compose.yml +414 -0
  10. package/docker-compose/generate-profiles.yaml +77 -0
  11. package/docker-compose/rpk-profile.yaml +24 -0
  12. package/docker-compose/transactions-schema.json +37 -0
  13. package/docker-compose/transactions.md +46 -0
  14. package/docker-compose/transform/README.adoc +73 -0
  15. package/docker-compose/transform/go.mod +5 -0
  16. package/docker-compose/transform/go.sum +2 -0
  17. package/docker-compose/transform/regex.wasm +0 -0
  18. package/docker-compose/transform/transform.go +122 -0
  19. package/docker-compose/transform/transform.yaml +33 -0
  20. package/extension-utils/compute-out.js +38 -0
  21. package/extension-utils/create-asciidoc-file.js +15 -0
  22. package/macros/data-template.js +2 -2
  23. package/package.json +15 -3
  24. package/tools/docusaurus-to-antora-conversion-scripts/convert-docs.sh +114 -0
  25. package/tools/docusaurus-to-antora-conversion-scripts/get-file-changes.sh +9 -0
  26. package/tools/docusaurus-to-antora-conversion-scripts/post-process-asciidoc.js +63 -0
  27. package/tools/docusaurus-to-antora-conversion-scripts/pre-process-markdown.js +108 -0
  28. package/tools/fetch-from-github.js +63 -0
  29. package/tools/gen-rpk-ascii.py +477 -0
  30. package/tools/get-console-version.js +53 -0
  31. package/tools/get-redpanda-version.js +53 -0
  32. package/tools/metrics/metrics.py +199 -0
  33. package/tools/metrics/requirements.txt +1 -0
  34. package/tools/property-extractor/Makefile +99 -0
  35. package/tools/property-extractor/README.adoc +206 -0
  36. package/tools/property-extractor/definitions.json +245 -0
  37. package/tools/property-extractor/file_pair.py +7 -0
  38. package/tools/property-extractor/json-to-asciidoc/generate_docs.py +460 -0
  39. package/tools/property-extractor/parser.py +224 -0
  40. package/tools/property-extractor/property_bag.py +4 -0
  41. package/tools/property-extractor/property_extractor.py +243 -0
  42. package/tools/property-extractor/requirements.txt +2 -0
  43. package/tools/property-extractor/tests/transformers_test.py +376 -0
  44. package/tools/property-extractor/transformers.py +397 -0
@@ -0,0 +1,397 @@
1
+ import re
2
+ from property_bag import PropertyBag
3
+ from parser import normalize_string
4
+
5
+
6
+ class BasicInfoTransformer:
7
+ def accepts(self, info, file_pair):
8
+ return True
9
+
10
+ def parse(self, property, info, file_pair):
11
+ property["name"] = info["params"][0]["value"]
12
+ property["defined_in"] = re.sub(
13
+ r"^.*src/", "src/", str(file_pair.implementation)
14
+ )
15
+ property["description"] = (
16
+ info["params"][1]["value"] if len(info["params"]) > 1 else None
17
+ )
18
+
19
+
20
+ class IsNullableTransformer:
21
+ def accepts(self, info, file_pair):
22
+ return True
23
+
24
+ def parse(self, property, info, file_pair):
25
+ if len(info["params"]) > 2 and "required" in info["params"][2]["value"]:
26
+ is_required = (
27
+ re.sub(r"^.*::", "", info["params"][2]["value"]["required"]) == "yes"
28
+ )
29
+ property["nullable"] = not is_required
30
+ elif "std::optional" in info["declaration"]:
31
+ property["nullable"] = True
32
+ else:
33
+ property["nullable"] = False
34
+
35
+ return property
36
+
37
+
38
+ class IsArrayTransformer:
39
+ def __init__(self, type_transformer):
40
+ self.type_transformer = type_transformer
41
+
42
+ def accepts(self, info, file_pair):
43
+ return "std::vector" in info["declaration"]
44
+
45
+ def parse(self, property, info, file_pair):
46
+ property["type"] = "array"
47
+ property["items"] = PropertyBag()
48
+ property["items"]["type"] = self.type_transformer.get_type_from_declaration(
49
+ info["declaration"]
50
+ )
51
+
52
+
53
+ class NeedsRestartTransformer:
54
+ def accepts(self, info, file_pair):
55
+ return True
56
+
57
+ def parse(self, property, info, file_pair):
58
+ needs_restart = "yes"
59
+ if len(info["params"]) > 2 and "needs_restart" in info["params"][2]["value"]:
60
+ needs_restart = re.sub(
61
+ r"^.*::", "", info["params"][2]["value"]["needs_restart"]
62
+ )
63
+ property["needs_restart"] = needs_restart != "no" # True by default, unless we find "no"
64
+
65
+ class GetsRestoredTransformer:
66
+ def accepts(self, info, file_pair):
67
+ # only run if the third param blob exists and has our flag
68
+ return (
69
+ len(info.get("params", [])) > 2
70
+ and isinstance(info["params"][2].get("value"), dict)
71
+ and "gets_restored" in info["params"][2]["value"]
72
+ )
73
+
74
+ def parse(self, property, info, file_pair):
75
+ raw = info["params"][2]["value"]["gets_restored"]
76
+ # strip off e.g. "gets_restored::no" → "no"
77
+ flag = re.sub(r"^.*::", "", raw)
78
+ # store as boolean
79
+ property["gets_restored"] = (flag != "no")
80
+
81
+
82
+ class VisibilityTransformer:
83
+ def accepts(self, info, file_pair):
84
+ return (
85
+ True
86
+ if len(info["params"]) > 2 and "visibility" in info["params"][2]["value"]
87
+ else False
88
+ )
89
+
90
+ def parse(self, property, info, file_pair):
91
+ property["visibility"] = re.sub(
92
+ r"^.*::", "", info["params"][2]["value"]["visibility"]
93
+ )
94
+
95
+
96
+ class TypeTransformer:
97
+ def accepts(self, info, file_pair):
98
+ return True
99
+
100
+ def get_cpp_type_from_declaration(self, declaration):
101
+ one_line_declaration = declaration.replace("\n", "").strip()
102
+ raw_type = (
103
+ re.sub(r"^.*property<(.+)>.*", "\\1", one_line_declaration)
104
+ .split()[0]
105
+ .replace(",", "")
106
+ )
107
+
108
+ if "std::optional" in raw_type:
109
+ raw_type = re.sub(".*std::optional<(.+)>.*", "\\1", raw_type)
110
+
111
+ if "std::vector" in raw_type:
112
+ raw_type = re.sub(".*std::vector<(.+)>.*", "\\1", raw_type)
113
+
114
+ return raw_type
115
+
116
+ def get_type_from_declaration(self, declaration):
117
+ raw_type = self.get_cpp_type_from_declaration(declaration)
118
+ type_mapping = [ # (regex, type)
119
+ ("^u(nsigned|int)", "integer"),
120
+ ("^(int|(std::)?size_t)", "integer"),
121
+ ("data_directory_path", "string"),
122
+ ("filesystem::path", "string"),
123
+ ("(double|float)", "number"),
124
+ ("string", "string"),
125
+ ("bool", "boolean"),
126
+ ("vector<[^>]+string>", "string[]"),
127
+ ("std::chrono", "integer"),
128
+ ]
129
+
130
+ for m in type_mapping:
131
+ if re.search(m[0], raw_type):
132
+ return m[1]
133
+
134
+ return raw_type
135
+
136
+ def parse(self, property, info, file_pair):
137
+ property["type"] = self.get_type_from_declaration(info["declaration"])
138
+ return property
139
+
140
+
141
+ class DeprecatedTransformer:
142
+ def accepts(self, info, file_pair):
143
+ return "deprecated_property" in info["declaration"] or (
144
+ len(info["params"]) > 2
145
+ and "visibility" in info["params"][2]["value"]
146
+ and "deprecated" in info["params"][2]["value"]["visibility"]
147
+ )
148
+
149
+ def parse(self, property, info, file_pair):
150
+ property["is_deprecated"] = True
151
+ property["type"] = None
152
+
153
+
154
+ class IsSecretTransformer:
155
+ def accepts(self, info, file_pair):
156
+ return (
157
+ True
158
+ if len(info["params"]) > 2 and "secret" in info["params"][2]["value"]
159
+ else False
160
+ )
161
+
162
+ def parse(self, property, info, file_pair):
163
+ is_secret = re.sub(r"^.*::", "", info["params"][2]["value"]["secret"])
164
+ property["is_secret"] = is_secret == "yes"
165
+
166
+
167
+ class NumericBoundsTransformer:
168
+ def __init__(self, type_transformer):
169
+ self.type_transformer = type_transformer
170
+
171
+ def accepts(self, info, file_pair):
172
+ type_str = self.type_transformer.get_cpp_type_from_declaration(info["declaration"])
173
+ return re.search("^(unsigned|u?int(8|16|32|64)?(_t)?)", type_str)
174
+
175
+ def parse(self, property, info, file_pair):
176
+ type_mapping = dict(
177
+ unsigned=(0, 2**32 - 1),
178
+ uint8_t=(0, 2**8 - 1),
179
+ uint16_t=(0, 2**16 - 1),
180
+ uint32_t=(0, 2**32 - 1),
181
+ uint64_t=(0, 2**64 - 1),
182
+ int=(-(2**31), 2**31 - 1),
183
+ int8_t=(-(2**7), 2**7 - 1),
184
+ int16_t=(-(2**15), 2**15 - 1),
185
+ int32_t=(-(2**31), 2**31 - 1),
186
+ int64_t=(-(2**63), 2**63 - 1),
187
+ )
188
+ type_str = self.type_transformer.get_cpp_type_from_declaration(info["declaration"])
189
+ if type_str in type_mapping:
190
+ property["minimum"] = type_mapping[type_str][0]
191
+ property["maximum"] = type_mapping[type_str][1]
192
+
193
+
194
+ class DurationBoundsTransformer:
195
+ def __init__(self, type_transformer):
196
+ self.type_transformer = type_transformer
197
+
198
+ def accepts(self, info, file_pair):
199
+ return re.search("std::chrono::", info["declaration"])
200
+
201
+ def parse(self, property, info, file_pair):
202
+ # Sizes based on: https://en.cppreference.com/w/cpp/chrono/duration
203
+ type_mapping = dict(
204
+ nanoseconds=(-(2**63), 2**63 - 1), # int 64
205
+ microseconds=(-(2**54), 2**54 - 1), # int 55
206
+ milliseconds=(-(2**44), 2**44 - 1), # int 45
207
+ seconds=(-(2**34), 2**34 - 1), # int 35
208
+ minutes=(-(2**28), 2**28 - 1), # int 29
209
+ hours=(-(2**22), 2**22 - 1), # int 23
210
+ days=(-(2**24), 2**24 - 1), # int 25
211
+ weeks=(-(2**21), 2**21 - 1), # int 22
212
+ months=(-(2**19), 2**19 - 1), # int 20
213
+ years=(-(2**16), 2**16 - 1), # int 17
214
+ )
215
+ type_str = self.type_transformer.get_cpp_type_from_declaration(info["declaration"])
216
+ duration_type = type_str.replace("std::chrono::", "")
217
+ if duration_type in type_mapping:
218
+ property["minimum"] = type_mapping[duration_type][0]
219
+ property["maximum"] = type_mapping[duration_type][1]
220
+
221
+
222
+ class SimpleDefaultValuesTransformer:
223
+ def accepts(self, info, file_pair):
224
+ # The default value is the 4th parameter.
225
+ return info["params"] and len(info["params"]) > 3
226
+
227
+ def parse(self, property, info, file_pair):
228
+ default = info["params"][3]["value"]
229
+
230
+ # Handle simple cases.
231
+ if default == "std::nullopt":
232
+ property["default"] = None
233
+ elif default == "{}":
234
+ pass
235
+ elif isinstance(default, PropertyBag):
236
+ property["default"] = default
237
+ elif re.search(r"^-?[0-9][0-9']*$", default): # integers (allow digit group separators)
238
+ property["default"] = int(re.sub(r"[^0-9-]", "", default))
239
+ elif re.search(r"^-?[0-9]+(\.[0-9]+)?$", default): # floats
240
+ property["default"] = float(re.sub(r"[^0-9.\-]", "", default))
241
+ elif re.search("^(true|false)$", default): # booleans
242
+ property["default"] = True if default == "true" else False
243
+ elif re.search(r"^\{[^:]+\}$", default): # string lists
244
+ property["default"] = [
245
+ normalize_string(s)
246
+ for s in re.sub(r"{([^}]+)}", r"\1", default).split(",")
247
+ ]
248
+ else:
249
+ # File sizes.
250
+ matches = re.search("^([0-9]+)_(.)iB$", default)
251
+ if matches:
252
+ size = int(matches.group(1))
253
+ unit = matches.group(2)
254
+ if unit == "K":
255
+ size = size * 1024
256
+ elif unit == "M":
257
+ size = size * 1024**2
258
+ elif unit == "G":
259
+ size = size * 1024**3
260
+ elif unit == "T":
261
+ size = size * 1024**4
262
+ elif unit == "P":
263
+ size = size * 1024**5
264
+ property["default"] = size
265
+ elif re.search("^(https|/[^/])", default): # URLs and paths
266
+ property["default"] = default
267
+ else:
268
+ # For durations, enums, or other default initializations.
269
+ if not re.search("([0-9]|::|\\()", default):
270
+ property["default"] = default
271
+ else:
272
+ property["default"] = default
273
+
274
+
275
+ class FriendlyDefaultTransformer:
276
+ """
277
+ Transforms C++ default expressions into a more user-friendly format for docs.
278
+ Handles cases like:
279
+ - std::numeric_limits<uint64_t>::max()
280
+ - std::chrono::seconds(15min)
281
+ - std::vector<ss::sstring>{"basic"}
282
+ - std::chrono::milliseconds(10)
283
+ - std::nullopt
284
+ """
285
+ def accepts(self, info, file_pair):
286
+ return info.get("params") and len(info["params"]) > 3
287
+
288
+ def parse(self, property, info, file_pair):
289
+ default = info["params"][3]["value"]
290
+
291
+ # Transform std::nullopt into None.
292
+ if "std::nullopt" in default:
293
+ property["default"] = None
294
+ return property
295
+
296
+ # Transform std::numeric_limits expressions.
297
+ if "std::numeric_limits" in default:
298
+ property["default"] = "Maximum value"
299
+ return property
300
+
301
+ # Transform std::chrono durations.
302
+ if "std::chrono" in default:
303
+ m = re.search(r"std::chrono::(\w+)\(([^)]+)\)", default)
304
+ if m:
305
+ unit = m.group(1)
306
+ value = m.group(2).strip()
307
+ property["default"] = f"{value} {unit}"
308
+ return property
309
+
310
+ # Transform std::vector defaults.
311
+ if "std::vector" in default:
312
+ m = re.search(r'\{([^}]+)\}', default)
313
+ if m:
314
+ contents = m.group(1).strip()
315
+ items = [item.strip(' "\'') for item in contents.split(',')]
316
+ property["default"] = items
317
+ return property
318
+
319
+ # Otherwise, leave the default as-is.
320
+ property["default"] = default
321
+ return property
322
+
323
+
324
+ class ExperimentalTransformer:
325
+ def accepts(self, info, file_pair):
326
+ return info.get("type") is not None and info["type"].startswith(("development_", "hidden_when_default_"))
327
+ def parse(self, property, info, file_pair):
328
+ property["is_experimental_property"] = True
329
+ return property
330
+
331
+
332
+ class AliasTransformer:
333
+ def accepts(self, info, file_pair):
334
+ if 'params' in info:
335
+ for param in info['params']:
336
+ if isinstance(param, dict) and 'value' in param:
337
+ value = param['value']
338
+ if isinstance(value, dict) and 'aliases' in value:
339
+ return True
340
+ return False
341
+
342
+ def parse(self, property, info, file_pair):
343
+ aliases = []
344
+ for param in info['params']:
345
+ value = param.get('value', {})
346
+ if isinstance(value, dict) and 'aliases' in value:
347
+ aliases_dict = value['aliases']
348
+ # Extract each alias, removing any surrounding braces or quotes.
349
+ aliases.extend(alias.strip('{}"') for alias in aliases_dict.values())
350
+ property['aliases'] = aliases
351
+
352
+
353
+ class EnterpriseTransformer:
354
+ def accepts(self, info, file_pair):
355
+ return bool(info.get('type') and 'enterprise' in info['type'])
356
+
357
+ def parse(self, property, info, file_pair):
358
+ if info['params'] is not None:
359
+ enterpriseValue = info['params'][0]['value']
360
+ property['enterprise_value'] = enterpriseValue
361
+ property['is_enterprise'] = True
362
+ del info['params'][0]
363
+ return property
364
+
365
+
366
+ class MetaParamTransformer:
367
+ def accepts(self, info, file_pair):
368
+ """
369
+ Check if the given info contains parameters that include a meta{...} value.
370
+ """
371
+ if 'params' in info:
372
+ for param in info['params']:
373
+ if isinstance(param, dict) and 'value' in param:
374
+ value = param['value']
375
+ if isinstance(value, str) and value.startswith("meta{"):
376
+ return True
377
+ return False
378
+
379
+ def parse(self, property, info, file_pair):
380
+ """
381
+ Transform into a structured dictionary.
382
+ """
383
+ if 'params' not in info or info['params'] is None:
384
+ return property
385
+
386
+ iterable_params = info['params']
387
+ for param in iterable_params:
388
+ if isinstance(param['value'], str) and param['value'].startswith("meta{"):
389
+ meta_content = param['value'].strip("meta{ }").strip()
390
+ meta_dict = {}
391
+ for item in meta_content.split(','):
392
+ item = item.strip()
393
+ if '=' in item:
394
+ key, value = item.split('=')
395
+ meta_dict[key.strip().replace('.', '')] = value.strip()
396
+ meta_dict['type'] = 'initializer_list' # Enforce required type
397
+ param['value'] = meta_dict