@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,108 @@
1
+ const fs = require('fs');
2
+ const { execSync } = require('child_process');
3
+ const pandoc = require('node-pandoc');
4
+ // Fail fast if required CLIs are missing
5
+ ['pandoc', 'kramdoc'].forEach(cmd => {
6
+ try {
7
+ execSync(`command -v ${cmd}`, { stdio: 'ignore' });
8
+ } catch {
9
+ console.error(`Required dependency "${cmd}" not found in PATH`);
10
+ process.exit(1);
11
+ }
12
+ });
13
+ const os = require('os');
14
+ const path = require('path');
15
+
16
+ function convertHtmlTableToAsciiDoc(htmlTable) {
17
+ return new Promise((resolve, reject) => {
18
+ pandoc(htmlTable, '-f html -t asciidoc', (err, result) => {
19
+ if (err) {
20
+ console.error(`Error converting HTML table to AsciiDoc: ${err.message}`);
21
+ resolve(htmlTable);
22
+ } else {
23
+ resolve(result);
24
+ }
25
+ });
26
+ });
27
+ }
28
+
29
+ function markdownToAsciidoc(markdown) {
30
+ const tempMarkdownPath = path.join(os.tmpdir(), 'temp_markdown.md');
31
+ fs.writeFileSync(tempMarkdownPath, markdown, 'utf-8');
32
+
33
+ let result;
34
+ try {
35
+ const command = `kramdoc -o - "${tempMarkdownPath}"`;
36
+ result = execSync(command, { encoding: 'utf-8' });
37
+ } catch (err) {
38
+ console.error(`Error converting Markdown to AsciiDoc: ${err.message}`);
39
+ result = markdown;
40
+ } finally {
41
+ fs.unlinkSync(tempMarkdownPath);
42
+ }
43
+ return result;
44
+ }
45
+
46
+ function processTabs(match) {
47
+ const tabItems = [...match.matchAll(/\s?<TabItem[^>]*value="([^"]+)"[^>]*label="([^"]+)"[^>]*>([\s\S]*?)<\/TabItem>/g)];
48
+
49
+ let result = ['\n<!--\n[tabs]'];
50
+ result.push('=====');
51
+ for (const tabItem of tabItems) {
52
+ const [_, value, label, content] = tabItem;
53
+ result.push(`${label}::`);
54
+ result.push('+');
55
+ result.push('--');
56
+ const asciidocContent = markdownToAsciidoc(content.trim(), '');
57
+ result.push(asciidocContent);
58
+ result.push('--');
59
+ }
60
+
61
+ result.push('=====');
62
+ result.push('-->');
63
+ return result.join('\n');
64
+ }
65
+
66
+ function processDetails(match) {
67
+ const detailsRegex = /<details>(?:\r?\n)<summary>([\s\S]*?)<\/summary>(?:\r?\n)([\s\S]*?)(?:\r?\n)<\/details>/g;
68
+
69
+ return match.replace(detailsRegex, (match, title, content) => {
70
+ const asciidocTitle = `.${title.trim()}`;
71
+ const asciidocBlock = `[%collapsible%]\n====\n${content.trim()}\n====`;
72
+
73
+ return `<!--\n${asciidocTitle}\n${asciidocBlock}\n-->`;
74
+ });
75
+ }
76
+
77
+ async function convertFile(file) {
78
+ const content = fs.readFileSync(file, 'utf-8');
79
+
80
+ var newContent = content.replace(/<Tabs>([\s\S]*?)<\/Tabs>/g, processTabs);
81
+ newContent = newContent.replace(/<details>([\s\S]*?)<\/details>/g, processDetails);
82
+
83
+ const htmlTableMatches = newContent.match(/\s?(<table>((.|\n)*?)<\/table>)/g);
84
+ if (htmlTableMatches) {
85
+ for (const htmlTableMatch of htmlTableMatches) {
86
+ const tableRegex = /(<table>((.|\n)*?)<\/table>)/;
87
+ const tableMatch = htmlTableMatch.match(tableRegex);
88
+ if (tableMatch) {
89
+ const htmlTable = tableMatch[0];
90
+ const asciidocTable = await convertHtmlTableToAsciiDoc(htmlTable);
91
+ newContent = newContent.replace(htmlTableMatch, `\n<!--\n${asciidocTable}\n-->`);
92
+ }
93
+ }
94
+ }
95
+
96
+ fs.writeFileSync(file, newContent, 'utf-8');
97
+ }
98
+
99
+ const inputFile = process.argv[2];
100
+ if (!inputFile) {
101
+ console.error('No input file provided');
102
+ process.exit(1);
103
+ }
104
+
105
+ convertFile(inputFile).catch((error) => {
106
+ console.error(`Error processing file: ${error.message}`);
107
+ process.exit(1);
108
+ });
@@ -0,0 +1,63 @@
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+
4
+ let octokitInstance = null;
5
+ async function loadOctokit() {
6
+ if (!octokitInstance) {
7
+ const { Octokit } = await import('@octokit/rest');
8
+ octokitInstance = process.env.VBOT_GITHUB_API_TOKEN
9
+ ? new Octokit({
10
+ auth: process.env.VBOT_GITHUB_API_TOKEN,
11
+ })
12
+ : new Octokit();
13
+
14
+ if (!process.env.VBOT_GITHUB_API_TOKEN) {
15
+ console.warn(
16
+ 'Warning: No GitHub token found (VBOT_GITHUB_API_TOKEN). API rate limits will be restricted.'
17
+ );
18
+ }
19
+ }
20
+ return octokitInstance;
21
+ }
22
+
23
+ async function saveFile(content, saveDir, filename) {
24
+ await fs.promises.mkdir(saveDir, { recursive: true });
25
+ const target = path.join(saveDir, filename);
26
+ await fs.promises.writeFile(target, content);
27
+ console.log(`Saved: ${target}`);
28
+ }
29
+
30
+ async function fetchFromGithub(owner, repo, remotePath, saveDir, customFilename) {
31
+ const octokit = await loadOctokit();
32
+
33
+ try {
34
+ const resp = await octokit.repos.getContent({ owner, repo, path: remotePath });
35
+ if (Array.isArray(resp.data)) {
36
+ // directory
37
+ for (const item of resp.data) {
38
+ if (item.type === 'file') {
39
+ await fetchFromGithub(owner, repo, item.path, saveDir, customFilename);
40
+ } else if (item.type === 'dir') {
41
+ // For directories, maintain the directory structure
42
+ const nestedDir = path.join(saveDir, path.basename(item.path));
43
+ await fetchFromGithub(owner, repo, item.path, nestedDir);
44
+ }
45
+ }
46
+ } else {
47
+ // single file
48
+ const content = Buffer.from(resp.data.content, 'base64').toString();
49
+ const filename = customFilename || path.basename(resp.data.path);
50
+ await saveFile(content, saveDir, filename);
51
+ }
52
+ } catch (error) {
53
+ if (error.status === 403 && error.message.includes('rate limit')) {
54
+ throw new Error(`GitHub API rate limit exceeded. Consider using a token via VBOT_GITHUB_API_TOKEN environment variable.`);
55
+ } else if (error.status === 404) {
56
+ throw new Error(`Path not found: ${remotePath} in ${owner}/${repo}`);
57
+ } else {
58
+ throw new Error(`Failed to fetch from GitHub: ${error.message}`);
59
+ }
60
+ }
61
+ }
62
+
63
+ module.exports = fetchFromGithub
@@ -0,0 +1,477 @@
1
+ import subprocess
2
+ import re
3
+ import json
4
+ import os
5
+ import filecmp
6
+ import difflib
7
+ import sys
8
+
9
+ suggestedReadings = """
10
+
11
+ ---
12
+
13
+ <SuggestedReading/>
14
+
15
+ - [Introducing rpk container](https://redpanda.com/blog/rpk-container/)
16
+ - [Getting started with rpk commands](https://redpanda.com/blog/getting-started-rpk/)
17
+ """
18
+
19
+
20
+ cmd_dict = {}
21
+ basic_commands_docker = ["docker","exec","-it"]
22
+ basic_commands_docker.append("redpanda-1")
23
+ rpk_basic_command = ["rpk"]
24
+
25
+ def assert_period(s):return s if s.endswith('.') else s + '.'
26
+
27
+ def escape_chars(s):
28
+
29
+ s = s.replace("./<timestamp>-bundle.zip", "./&lt;timestamp&gt;-bundle.zip")
30
+ return s
31
+
32
+ def cmp_rpk_ascii(dir1, dir2, outdir=""):
33
+ if outdir:
34
+ for root1, _, files1 in os.walk(dir1):
35
+ for file1 in files1:
36
+ for root2, _, files2 in os.walk(dir2):
37
+ for file2 in files2:
38
+ if file1 == file2:
39
+ file_path1 = os.path.join(root1, file1)
40
+ file_path2 = os.path.join(root2, file2)
41
+ if not filecmp.cmp(file_path1, file_path2, shallow=False):
42
+ with open(file_path1, "r") as f1, open(file_path2, "r") as f2:
43
+ lines1 = f1.readlines()
44
+ lines2 = f2.readlines()
45
+ diff = difflib.unified_diff(lines1, lines2, file_path1, file_path2)
46
+ outfile = outdir + "/diff_" + os.path.basename(file_path1)
47
+ with open(outfile, "w") as fo:
48
+ fo.write("\n".join(diff))
49
+ print("Wrote " + outfile)
50
+
51
+ class Flag:
52
+ def __init__(self, value: str, flag_type: str, explanation: str):
53
+ self.value = value
54
+ self.flag_type = flag_type
55
+ self.explanation = explanation
56
+
57
+
58
+ # Execute a subprocess inside a Linux machine. If the command is multi level (such as rpk acl create) it generates a bigger list
59
+ def execute_process(commands):
60
+ if len(commands) > 0:
61
+ commands = commands[0].split(" ")
62
+ commands_to_execute = basic_commands_docker + rpk_basic_command + commands
63
+ commands_to_execute.append("-h")
64
+ try:
65
+ process = subprocess.run(
66
+ commands_to_execute,
67
+ stdout=subprocess.PIPE,
68
+ stderr=subprocess.PIPE,
69
+ check=True,
70
+ )
71
+ return process.stdout.decode("utf-8")
72
+ except subprocess.CalledProcessError as e:
73
+ print(f"Error executing command: {' '.join(commands_to_execute)}")
74
+ print(f"Error message: {e.stderr.decode('utf-8')}")
75
+ sys.exit(1)
76
+
77
+
78
+ # Get the explanation written before the usage. Example:
79
+ """ # rpk is the Redpanda CLI & toolbox.
80
+ # Usage:
81
+ # rpk [command] """
82
+
83
+
84
+ def get_explanation(process_line):
85
+ explanation_line = process_line[: process_line.find("Usage")].rstrip("\n").strip()
86
+ explanation_line = explanation_line.replace("redpanda.yaml","`redpanda.yaml`")
87
+ return explanation_line
88
+
89
+
90
+ # Get the usage of the command. If it's an initial command, look for available commands. If it's a final command, then look for flags. Finally if neither are present, extract the usage. Example:
91
+ """ Usage:
92
+ rpk [command]
93
+
94
+ Available Commands:
95
+ acl Manage ACLs and SASL users.
96
+ cluster Interact with a Redpanda cluster.
97
+ container Manage a local container cluster.
98
+ debug Debug the local Redpanda process.
99
+ generate Generate a configuration template for related services.
100
+ group Describe, list, and delete consumer groups and manage their offsets.
101
+ help Help about any command
102
+ iotune Measure filesystem performance and create IO configuration file.
103
+ plugin List, download, update, and remove rpk plugins.
104
+ redpanda Interact with a local Redpanda process
105
+ topic Create, delete, produce to and consume from Redpanda topics.
106
+ version Check the current version.
107
+ wasm Deploy and remove inline WASM engine scripts. """
108
+
109
+
110
+ def get_usage(process_line):
111
+ if process_line.find("Available Commands:") != -1:
112
+ return process_line[
113
+ process_line.find("Usage") : process_line.find("Available Commands:")
114
+ ].rstrip("\n")
115
+ elif process_line.find("Flags:") != -1:
116
+ return process_line[
117
+ process_line.find("Usage") : process_line.find("Flags:")
118
+ ].rstrip("\n")
119
+ else:
120
+ return process_line[process_line.find("Usage") :].rstrip("\n")
121
+
122
+
123
+ # Get lines for possible available commands. Example:
124
+ """ Usage:
125
+ rpk [command]
126
+
127
+ Available Commands:
128
+ acl Manage ACLs and SASL users.
129
+ cluster Interact with a Redpanda cluster.
130
+ container Manage a local container cluster.
131
+ debug Debug the local Redpanda process.
132
+ generate Generate a configuration template for related services.
133
+ group Describe, list, and delete consumer groups and manage their offsets.
134
+ help Help about any command
135
+ iotune Measure filesystem performance and create IO configuration file.
136
+ plugin List, download, update, and remove rpk plugins.
137
+ redpanda Interact with a local Redpanda process
138
+ topic Create, delete, produce to and consume from Redpanda topics.
139
+ version Check the current version.
140
+ wasm Deploy and remove inline WASM engine scripts. """
141
+
142
+
143
+ def get_commands(process_line):
144
+ if process_line.find('Use "rpk') != -1:
145
+ full_command = process_line[
146
+ process_line.find("Available Commands:") : process_line.find('Use "rpk')
147
+ ]
148
+ else:
149
+ full_command = process_line[process_line.find("Available Commands:") :]
150
+ commands = full_command[: full_command.find("Flags:")]
151
+ prefix = "Available Commands:"
152
+ return commands[len(prefix):] if commands.startswith(prefix) else commands
153
+
154
+ # Extract lines for the flags from a command. Example:
155
+ """ Available Commands:
156
+ acl Manage ACLs and SASL users.
157
+ cluster Interact with a Redpanda cluster.
158
+ container Manage a local container cluster.
159
+ debug Debug the local Redpanda process.
160
+ generate Generate a configuration template for related services.
161
+ group Describe, list, and delete consumer groups and manage their offsets.
162
+ help Help about any command
163
+ iotune Measure filesystem performance and create IO configuration file.
164
+ plugin List, download, update, and remove rpk plugins.
165
+ redpanda Interact with a local Redpanda process
166
+ topic Create, delete, produce to and consume from Redpanda topics.
167
+ version Check the current version.
168
+ wasm Deploy and remove inline WASM engine scripts.
169
+
170
+ Flags:
171
+ -h, --help help for rpk
172
+ -v, --verbose Enable verbose logging (default: false).
173
+
174
+ Use "rpk [command] --help" for more information about a command. """
175
+
176
+
177
+ def extract_flags(process_line):
178
+ if process_line.find('Use "rpk') != -1:
179
+ flag_line = process_line[process_line.find("Flags:") : process_line.find('Use "rpk')]
180
+ else:
181
+ flag_line = process_line[process_line.find("Flags:") :]
182
+ flag_line = flag_line.replace("\"/var/lib/redpanda/.config/rpk/rpk.yaml\"","`/var/lib/redpanda/.config/rpk/rpk.yaml`")
183
+ flag_line = flag_line.replace("$PWD/redpanda.yaml","`$PWD/redpanda.yaml`")
184
+ flag_line = flag_line.replace("/etc/redpanda/redpanda.yaml","`/etc/redpanda/redpanda.yaml`")
185
+ return flag_line
186
+
187
+
188
+ # Extract new commands (multilevel) or flags from the available ones. Example:
189
+ """ deploy Deploy inline WASM function.
190
+ generate Create an npm template project for inline WASM engine.
191
+ remove Remove inline WASM function. """
192
+ # or
193
+ """ --brokers strings Comma-separated list of broker ip:port pairs (e.g. --brokers '192.168.78.34:9092,192.168.78.35:9092,192.179.23.54:9092' ). Alternatively, you may set the REDPANDA_BROKERS environment variable with the comma-separated list of broker addresses.
194
+ --config string Redpanda config file, if not set the file will be searched for in the default locations
195
+ -h, --help help for wasm
196
+ --password string SASL password to be used for authentication.
197
+ --sasl-mechanism string The authentication mechanism to use. Supported values: SCRAM-SHA-256, SCRAM-SHA-512.
198
+ --tls-cert string The certificate to be used for TLS authentication with the broker.
199
+ --tls-enabled Enable TLS for the Kafka API (not necessary if specifying custom certs).
200
+ --tls-key string The certificate key to be used for TLS authentication with the broker.
201
+ --tls-truststore string The truststore to be used for TLS communication with the broker.
202
+ --user string SASL user to be used for authentication. """
203
+
204
+
205
+ def extract_new_commands(available, is_flag):
206
+ iterable_commands = []
207
+ mline = ""
208
+
209
+ for line in available.splitlines():
210
+ if not line:
211
+ if mline:
212
+ mline = mline.strip()
213
+ iterable_commands.append(mline)
214
+ mline = ""
215
+ continue
216
+ if not is_flag:
217
+ if mline:
218
+ mline = mline.strip()
219
+ iterable_commands.append(mline)
220
+ mline = ""
221
+ iterable_commands.append(line.split(" ")[2])
222
+ else:
223
+ if line.strip().startswith("-"):
224
+ if mline:
225
+ mline = mline.strip()
226
+ iterable_commands.append(mline)
227
+ mline = ""
228
+ mline = line[line.find("-") :].strip()
229
+ continue
230
+ elif line[0] != " ":
231
+ if mline:
232
+ mline = mline.strip()
233
+ iterable_commands.append(mline)
234
+ mline = ""
235
+ continue
236
+ else:
237
+ mline += " " + line.strip()
238
+ continue
239
+
240
+ if mline:
241
+ mline = mline.strip()
242
+ iterable_commands.append(mline.strip())
243
+
244
+ return iterable_commands
245
+
246
+
247
+ # Extract flag value, explanation and type from a flag line. Example:
248
+ """--user string SASL user to be used for authentication. """
249
+
250
+
251
+ def extract_all_flag(line):
252
+ flag_set = []
253
+ for flag in line:
254
+ value = flag[: flag.find(" ")]
255
+ explanation = flag[flag.find(" ") :]
256
+ if value.find(",") != -1:
257
+ explanation = explanation.lstrip(" ")
258
+ value = value + " " + (explanation[: explanation.find(" ")])
259
+ explanation = explanation[explanation.find(" ") :]
260
+
261
+ if re.search(r"\bstring\b", explanation):
262
+ flag_type = "string"
263
+ explanation = re.sub(r"\bstring\b", "", explanation)
264
+ elif re.search(r"\bstrings\b", flag):
265
+ flag_type = "strings"
266
+ explanation = re.sub(r"\bstrings\b", "", explanation)
267
+ elif re.search(r"\bstringArray\b", flag):
268
+ flag_type = "stringArray"
269
+ explanation = re.sub(r"\bstringArray\b", "", explanation)
270
+ elif re.search(r"\bint\b", flag):
271
+ flag_type = "int"
272
+ explanation = re.sub(r"\bint\b", "", explanation)
273
+ elif re.search(r"\bint16\b", flag):
274
+ flag_type = "int16"
275
+ explanation = re.sub(r"\bint16\b", "", explanation)
276
+ elif re.search(r"\bint32\b", flag):
277
+ flag_type = "int32"
278
+ explanation = re.sub(r"\bint32\b", "", explanation)
279
+ elif re.search(r"\bint32Slice\b", flag):
280
+ flag_type = "int32"
281
+ explanation = re.sub(r"\bint32Array\b", "", explanation)
282
+ elif re.search(r"\bduration\b", flag):
283
+ flag_type = "duration"
284
+ explanation = re.sub(r"\bduration\b", "", explanation)
285
+ else:
286
+ flag_type = "-"
287
+ explanation = assert_period(explanation.strip())
288
+ flag_set.append(Flag(value, flag_type, explanation))
289
+ return flag_set
290
+
291
+ # Build dictionary of commands
292
+ def build_dict(cmd_dict, executed_command, explanation, usage, it_flags, flag_list):
293
+
294
+ cmd = {"description" : explanation, "usage" : usage, "flags" : {} }
295
+
296
+ if it_flags:
297
+ for flag in flag_list:
298
+ cmd['flags'][flag.value] = { "type" : flag.flag_type.strip(), "description" : flag.explanation}
299
+
300
+ cmd_dict[executed_command] = cmd
301
+ return cmd_dict
302
+
303
+
304
+ def build_ascii(ascii_result, executed_command, explanation, usage, it_flags, flag_list, separate_files):
305
+ # Determine the version passed as argument or default to "latest"
306
+ tag_modified = sys.argv[1] if len(sys.argv) > 1 else "latest"
307
+ # Build the output directory as "autogenerated/<tag_modified>/rpk"
308
+ rpk_gen_dir = os.path.join("autogenerated", tag_modified, "rpk")
309
+ if not os.path.exists(rpk_gen_dir):
310
+ os.makedirs(rpk_gen_dir)
311
+
312
+ # Construct the asciidoc content
313
+ ascii_result += "= " + executed_command
314
+ ascii_result += "\n:description: " + executed_command
315
+ ascii_result += "\n\n" + explanation
316
+
317
+ usage_val_start = usage.find("Usage:") + len("Usage:")
318
+ aliases_start = usage.find("Aliases:")
319
+ aliases_val_start = usage.find("Aliases:") + len("Aliases:")
320
+
321
+ usage_val = usage[usage_val_start:aliases_start] if aliases_start >= 0 else usage[usage_val_start:]
322
+ ascii_result += "\n\n== Usage"
323
+ ascii_result += "\n\n[,bash]\n"
324
+ ascii_result += "----\n"
325
+ ascii_result += usage_val.strip()
326
+ ascii_result += "\n----"
327
+
328
+ if aliases_start >= 0:
329
+ ascii_result += "\n\n== Aliases"
330
+ ascii_result += "\n\n[,bash]\n"
331
+ ascii_result += "----\n"
332
+ ascii_result += usage[aliases_val_start:].strip()
333
+ ascii_result += "\n----"
334
+
335
+ if it_flags:
336
+ ascii_result += "\n\n== Flags"
337
+ ascii_result += "\n\n[cols=\"1m,1a,2a\"]"
338
+ ascii_result += "\n|==="
339
+ ascii_result += "\n|*Value* |*Type* |*Description*"
340
+
341
+ for flag in flag_list:
342
+ ascii_result += "\n\n"
343
+ ascii_result += "|" + flag.value + " |"
344
+ ascii_result += flag.flag_type + " |"
345
+ ascii_result += flag.explanation
346
+
347
+ ascii_result += "\n|==="
348
+
349
+ # Build filename using os.path.join rather than string concatenation.
350
+ filename = os.path.join(rpk_gen_dir, executed_command.replace(" ", "-") + ".adoc")
351
+
352
+ # If the file exists, delete it.
353
+ if os.path.exists(filename):
354
+ os.remove(filename)
355
+
356
+ # Write the ASCII content to the file with the necessary escaping.
357
+ with open(filename, "w", encoding="utf-8") as filetowrite:
358
+ ascii_result = escape_chars(ascii_result)
359
+ filetowrite.write(ascii_result)
360
+
361
+ return ascii_result
362
+
363
+ ## run basic command first
364
+ first_command = basic_commands_docker + rpk_basic_command + ["version"]
365
+ result = subprocess.run(first_command, stdout=subprocess.PIPE)
366
+ rpk_version = result.stdout.decode('utf-8').strip(" \n")
367
+
368
+ result = execute_process([])
369
+
370
+ explanation = get_explanation(result)
371
+
372
+ usage = get_usage(result)
373
+
374
+ full_command = get_commands(result)
375
+
376
+ commands = full_command[: full_command.find("Flags:")]
377
+
378
+ available_commmands = commands.lstrip("Available Commands:")
379
+ it_commands = extract_new_commands(available_commmands, False)
380
+
381
+ flags = extract_flags(result)
382
+ available_flags = flags.lstrip("Flags:")
383
+
384
+ it_flags = extract_new_commands(available_flags, True)
385
+ flag_list = extract_all_flag(it_flags)
386
+
387
+ md_result = """---
388
+ title: rpk commands
389
+ rpk_version: """ + rpk_version + """
390
+ ---
391
+
392
+ `rpk` is Redpanda's command line interface (CLI) utility. rpk commands allow you to configure and manage Redpanda clusters, tune them for better performance, manage topics and groups, manage access control lists (ACLs).
393
+
394
+ This section lists each rpk command in alphabetical order, along with a table of flags for that command. All descriptions are from the output of the `rpk <command> -–help` command.
395
+
396
+ """
397
+
398
+ executed_command = "rpk"
399
+ quantity =0
400
+
401
+ for command in it_commands:
402
+ quantity+=1
403
+
404
+ result = execute_process([command])
405
+ executed_command = "rpk " + command
406
+
407
+ explanation = get_explanation(result)
408
+
409
+ usage = get_usage(result)
410
+
411
+ full_command = get_commands(result)
412
+
413
+ commands = full_command[: full_command.find("Flags:")]
414
+
415
+ available_commmands = commands.lstrip("Available Commands:")
416
+ new_commands = extract_new_commands(available_commmands, False)
417
+
418
+ flags = extract_flags(result)
419
+ available_flags = flags.lstrip("Flags:")
420
+
421
+ it_flags = extract_new_commands(available_flags, True)
422
+
423
+ flag_list = extract_all_flag(it_flags)
424
+
425
+ cmd_dict = build_dict(cmd_dict, executed_command, explanation, usage, it_flags, flag_list);
426
+
427
+ md_result = build_ascii(
428
+ "", executed_command, explanation, usage, it_flags, flag_list, True
429
+ )
430
+
431
+ index = it_commands.index(command) + 1
432
+ for new_command in new_commands:
433
+ it_commands.insert(index, command + " " + new_command)
434
+ index += 1
435
+
436
+ if(quantity%20==0):
437
+ print(f"{quantity}/{len(it_commands)} files written in disk.")
438
+
439
+ cmd_dict['rpk_version'] = rpk_version
440
+ json_object = json.dumps(cmd_dict, indent = 4)
441
+
442
+ md_result = md_result.replace(
443
+ """ rpk-<name>
444
+ rpk.ac-<name>""",
445
+ """```bash
446
+ rpk-<name>
447
+ rpk.ac-<name>
448
+ ```
449
+ """,
450
+ )
451
+
452
+
453
+
454
+ md_result = md_result + suggestedReadings
455
+
456
+ # Get the version from command-line arguments, defaulting to "latest" if not provided.
457
+ tag_modified = sys.argv[1] if len(sys.argv) > 1 else "latest"
458
+
459
+ # Build a common base directory using the version.
460
+ base_dir = os.path.join("autogenerated", tag_modified, "rpk")
461
+
462
+ # Write the JSON file into a JSON subdirectory within the same base directory.
463
+ json_path = os.path.join(base_dir, "json")
464
+ if not os.path.exists(json_path):
465
+ os.makedirs(json_path)
466
+
467
+ json_file = os.path.join(json_path, "rpk-commands.json")
468
+ try:
469
+ with open(json_file, "w") as filetowrite:
470
+ filetowrite.write(json_object)
471
+ print("The rpk commands have been successfully generated at", json_file)
472
+ except Exception as e:
473
+ print("Error generating the rpk commands file " + str(e))
474
+
475
+ # Define the directories for comparison.
476
+ dir1 = os.path.join("docs", "reference", "rpk")
477
+ dir2 = base_dir # Using the common base directory