@redpanda-data/docs-extensions-and-macros 5.0.4 → 5.1.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-mcp.js +30 -2
- package/bin/doc-tools.js +260 -51
- package/bin/mcp-tools/rpk-docs.js +109 -39
- package/extensions/version-fetcher/set-latest-version.js +0 -131
- package/package.json +2 -2
- package/tools/rpk-docs/DEPRECATION-SCANNING.md +192 -0
- package/tools/rpk-docs/extract-conditional-content.js +169 -0
- package/tools/rpk-docs/extract-overrides.js +566 -0
- package/tools/rpk-docs/generate-rpk-docs.js +2433 -0
- package/tools/rpk-docs/helpers/index.js +458 -0
- package/tools/rpk-docs/report-delta.js +479 -0
- package/tools/rpk-docs/rpk-docs-handler.js +1650 -0
- package/tools/rpk-docs/scan-deprecated-commands.js +295 -0
- package/tools/rpk-docs/templates/command.hbs +576 -0
- package/tools/rpk-docs/templates/content-item.hbs +106 -0
- package/tools/rpk-docs/templates/subsection.hbs +37 -0
- package/tools/rpk-docs/validate-output.js +414 -0
- package/tools/rpk-docs/validate-overrides.js +624 -0
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Scan rpk Go source code for hidden and deprecated commands
|
|
3
|
+
*
|
|
4
|
+
* Extracts commands marked with Hidden: true or Deprecated: "message"
|
|
5
|
+
* to ensure they're included in documentation with proper deprecation notices.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const fs = require('fs');
|
|
9
|
+
const path = require('path');
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Scan a Go file for cobra command definitions with deprecation/hidden status
|
|
13
|
+
* @param {string} filePath - Path to Go file
|
|
14
|
+
* @returns {Object|null} Command metadata if deprecated/hidden, null otherwise
|
|
15
|
+
*/
|
|
16
|
+
function scanGoFile(filePath) {
|
|
17
|
+
const content = fs.readFileSync(filePath, 'utf8');
|
|
18
|
+
|
|
19
|
+
// Look for NewCommand function that returns *cobra.Command
|
|
20
|
+
const newCommandMatch = content.match(/func NewCommand\([^)]*\)\s*\*cobra\.Command\s*\{/);
|
|
21
|
+
if (!newCommandMatch) {
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// Extract the cobra.Command struct definition
|
|
26
|
+
// Handle nested braces and multiline strings
|
|
27
|
+
let cmdBlock = '';
|
|
28
|
+
let braceCount = 0;
|
|
29
|
+
let startIndex = content.indexOf('&cobra.Command{');
|
|
30
|
+
|
|
31
|
+
if (startIndex === -1) {
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
startIndex += '&cobra.Command{'.length;
|
|
36
|
+
for (let i = startIndex; i < content.length; i++) {
|
|
37
|
+
const char = content[i];
|
|
38
|
+
if (char === '{') braceCount++;
|
|
39
|
+
if (char === '}') {
|
|
40
|
+
if (braceCount === 0) break;
|
|
41
|
+
braceCount--;
|
|
42
|
+
}
|
|
43
|
+
cmdBlock += char;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Extract fields - handle multiline strings with backticks or quotes
|
|
47
|
+
const useMatch = cmdBlock.match(/Use:\s*"([^"]+)"/);
|
|
48
|
+
const shortMatch = cmdBlock.match(/Short:\s*"([^"]+)"/);
|
|
49
|
+
const longMatch = cmdBlock.match(/Long:\s*`([^`]+)`|Long:\s*"([^"]+)"/s);
|
|
50
|
+
const hiddenMatch = cmdBlock.match(/Hidden:\s*true/);
|
|
51
|
+
const deprecatedMatch = cmdBlock.match(/Deprecated:\s*"([^"]+(?:[^"\\]|\\.)*)"/s);
|
|
52
|
+
|
|
53
|
+
// Only return if hidden or deprecated
|
|
54
|
+
if (!hiddenMatch && !deprecatedMatch) {
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const result = {
|
|
59
|
+
use: useMatch ? useMatch[1] : null,
|
|
60
|
+
short: shortMatch ? shortMatch[1] : null,
|
|
61
|
+
long: longMatch ? (longMatch[1] || longMatch[2]) : null,
|
|
62
|
+
hidden: !!hiddenMatch,
|
|
63
|
+
deprecated: !!deprecatedMatch,
|
|
64
|
+
deprecatedMessage: deprecatedMatch ? deprecatedMatch[1].replace(/\n\s*/g, ' ').trim() : null
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
return result;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Build command path from directory structure
|
|
72
|
+
* @param {string} filePath - Path to Go file (relative to rpk root)
|
|
73
|
+
* @param {string} rpkRoot - Path to rpk source root
|
|
74
|
+
* @returns {string} Command path (e.g., "rpk redpanda admin")
|
|
75
|
+
*/
|
|
76
|
+
function buildCommandPath(filePath, rpkRoot) {
|
|
77
|
+
const relativePath = path.relative(rpkRoot, filePath);
|
|
78
|
+
const parts = relativePath.split(path.sep);
|
|
79
|
+
|
|
80
|
+
// Remove filename, and filter out structural directories
|
|
81
|
+
// Structure is either:
|
|
82
|
+
// pkg/cli/<group>/<command>/file.go
|
|
83
|
+
// cmd/rpk/<command>/file.go
|
|
84
|
+
const commandParts = [];
|
|
85
|
+
let inCommandPath = false;
|
|
86
|
+
|
|
87
|
+
for (const part of parts) {
|
|
88
|
+
if (part.endsWith('.go')) continue;
|
|
89
|
+
|
|
90
|
+
// Start collecting after 'cli' or after 'rpk' in cmd
|
|
91
|
+
if (part === 'cli' || (part === 'rpk' && parts.includes('cmd'))) {
|
|
92
|
+
inCommandPath = true;
|
|
93
|
+
continue;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
if (inCommandPath) {
|
|
97
|
+
commandParts.push(part);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Build path: rpk + directory hierarchy
|
|
102
|
+
return 'rpk ' + commandParts.join(' ');
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Recursively scan directory for deprecated commands
|
|
107
|
+
* @param {string} dir - Directory to scan
|
|
108
|
+
* @param {string} rpkRoot - Path to rpk source root
|
|
109
|
+
* @param {Array} results - Accumulator for results
|
|
110
|
+
*/
|
|
111
|
+
function scanDirectory(dir, rpkRoot, results = []) {
|
|
112
|
+
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
113
|
+
|
|
114
|
+
for (const entry of entries) {
|
|
115
|
+
const fullPath = path.join(dir, entry.name);
|
|
116
|
+
|
|
117
|
+
if (entry.isDirectory()) {
|
|
118
|
+
scanDirectory(fullPath, rpkRoot, results);
|
|
119
|
+
} else if (entry.isFile() && entry.name.endsWith('.go')) {
|
|
120
|
+
const metadata = scanGoFile(fullPath);
|
|
121
|
+
if (metadata) {
|
|
122
|
+
const commandPath = buildCommandPath(fullPath, rpkRoot);
|
|
123
|
+
results.push({
|
|
124
|
+
commandPath,
|
|
125
|
+
filePath: path.relative(rpkRoot, fullPath),
|
|
126
|
+
...metadata
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
return results;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Extract deprecation info and convert to override format
|
|
137
|
+
* @param {Object} cmdMetadata - Command metadata from source scan
|
|
138
|
+
* @returns {Object} Override object for rpk-overrides.json
|
|
139
|
+
*/
|
|
140
|
+
function buildOverrideFromDeprecation(cmdMetadata) {
|
|
141
|
+
const override = {
|
|
142
|
+
deprecated: true,
|
|
143
|
+
_note: `Hidden: ${cmdMetadata.hidden}, found by scanning Go source`
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
if (cmdMetadata.deprecatedMessage) {
|
|
147
|
+
// Parse the deprecation message to extract replacement info
|
|
148
|
+
const message = cmdMetadata.deprecatedMessage;
|
|
149
|
+
|
|
150
|
+
// Common patterns:
|
|
151
|
+
// "use `rpk cluster` subcommands; see ..."
|
|
152
|
+
// "use `rpk cluster brokers` instead"
|
|
153
|
+
|
|
154
|
+
if (message.includes('use `') && message.includes('`')) {
|
|
155
|
+
// Extract the replacement command
|
|
156
|
+
const cmdMatch = message.match(/use `([^`]+)`/);
|
|
157
|
+
if (cmdMatch) {
|
|
158
|
+
const replacementCmd = cmdMatch[1];
|
|
159
|
+
|
|
160
|
+
// Try to build xref
|
|
161
|
+
if (replacementCmd.startsWith('rpk ')) {
|
|
162
|
+
const parts = replacementCmd.split(' ');
|
|
163
|
+
const xrefPath = parts.slice(1).join('/');
|
|
164
|
+
const filename = parts.slice(1).join('-');
|
|
165
|
+
override.replacement = `Use xref:reference:rpk/rpk-${xrefPath}/rpk-${filename}.adoc[\`${replacementCmd}\`] instead.`;
|
|
166
|
+
} else {
|
|
167
|
+
override.replacement = `Use \`${replacementCmd}\` instead.`;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// Add full deprecation message if it has additional context
|
|
172
|
+
if (message.length > 50 || message.includes(';')) {
|
|
173
|
+
override.deprecatedMessage = message;
|
|
174
|
+
}
|
|
175
|
+
} else {
|
|
176
|
+
override.deprecatedMessage = message;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
return override;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Scan rpk source for deprecated/hidden commands
|
|
185
|
+
* @param {string} sourcePath - Path to rpk source (src/go/rpk)
|
|
186
|
+
* @returns {Object} Map of command paths to override metadata
|
|
187
|
+
*/
|
|
188
|
+
function scanDeprecatedCommands(sourcePath) {
|
|
189
|
+
console.log('Scanning Go source for deprecated/hidden commands...');
|
|
190
|
+
|
|
191
|
+
// Scan both cmd/rpk (old structure) and pkg/cli (current structure)
|
|
192
|
+
const dirsToScan = [
|
|
193
|
+
path.join(sourcePath, 'cmd', 'rpk'),
|
|
194
|
+
path.join(sourcePath, 'pkg', 'cli')
|
|
195
|
+
];
|
|
196
|
+
|
|
197
|
+
const results = [];
|
|
198
|
+
|
|
199
|
+
for (const dir of dirsToScan) {
|
|
200
|
+
if (fs.existsSync(dir)) {
|
|
201
|
+
console.log(`Scanning ${dir}...`);
|
|
202
|
+
scanDirectory(dir, sourcePath, results);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
console.log(`Found ${results.length} deprecated/hidden command(s):`);
|
|
207
|
+
|
|
208
|
+
const overrides = {};
|
|
209
|
+
for (const cmd of results) {
|
|
210
|
+
console.log(` - ${cmd.commandPath}`);
|
|
211
|
+
if (cmd.deprecatedMessage) {
|
|
212
|
+
console.log(` Deprecated: ${cmd.deprecatedMessage.substring(0, 80)}${cmd.deprecatedMessage.length > 80 ? '...' : ''}`);
|
|
213
|
+
}
|
|
214
|
+
if (cmd.hidden) {
|
|
215
|
+
console.log(` Hidden: true`);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
overrides[cmd.commandPath] = buildOverrideFromDeprecation(cmd);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
return overrides;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* Merge deprecated commands into existing tree
|
|
226
|
+
* @param {Object} tree - Command tree from rpk --print-tree
|
|
227
|
+
* @param {Object} deprecatedOverrides - Overrides from source scanning
|
|
228
|
+
* @param {Object} existingOverrides - Existing overrides.json data
|
|
229
|
+
* @returns {Object} Tree with deprecated commands added
|
|
230
|
+
*/
|
|
231
|
+
function mergeDeprecatedCommands(tree, deprecatedOverrides, existingOverrides) {
|
|
232
|
+
// For now, just return the overrides to be merged into overrides.json
|
|
233
|
+
// The actual command data will come from the old docs or manual creation
|
|
234
|
+
|
|
235
|
+
console.log('\nMerging deprecated commands into overrides...');
|
|
236
|
+
|
|
237
|
+
if (!existingOverrides.commands) {
|
|
238
|
+
existingOverrides.commands = {};
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
let added = 0;
|
|
242
|
+
let updated = 0;
|
|
243
|
+
|
|
244
|
+
for (const [cmdPath, metadata] of Object.entries(deprecatedOverrides)) {
|
|
245
|
+
if (!existingOverrides.commands[cmdPath]) {
|
|
246
|
+
existingOverrides.commands[cmdPath] = metadata;
|
|
247
|
+
added++;
|
|
248
|
+
} else {
|
|
249
|
+
// Merge, keeping existing content but adding deprecation metadata
|
|
250
|
+
existingOverrides.commands[cmdPath] = {
|
|
251
|
+
...existingOverrides.commands[cmdPath],
|
|
252
|
+
deprecated: metadata.deprecated,
|
|
253
|
+
replacement: metadata.replacement || existingOverrides.commands[cmdPath].replacement,
|
|
254
|
+
deprecatedMessage: metadata.deprecatedMessage || existingOverrides.commands[cmdPath].deprecatedMessage,
|
|
255
|
+
_note: metadata._note
|
|
256
|
+
};
|
|
257
|
+
updated++;
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
console.log(`Added ${added} new deprecated commands`);
|
|
262
|
+
console.log(`Updated ${updated} existing commands with deprecation info`);
|
|
263
|
+
|
|
264
|
+
return existingOverrides;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
module.exports = {
|
|
268
|
+
scanDeprecatedCommands,
|
|
269
|
+
mergeDeprecatedCommands,
|
|
270
|
+
scanGoFile,
|
|
271
|
+
buildCommandPath
|
|
272
|
+
};
|
|
273
|
+
|
|
274
|
+
// CLI usage
|
|
275
|
+
if (require.main === module) {
|
|
276
|
+
const sourcePath = process.argv[2];
|
|
277
|
+
const overridesPath = process.argv[3];
|
|
278
|
+
|
|
279
|
+
if (!sourcePath) {
|
|
280
|
+
console.error('Usage: node scan-deprecated-commands.js <rpk-source-path> [overrides-json-path]');
|
|
281
|
+
process.exit(1);
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
const deprecated = scanDeprecatedCommands(sourcePath);
|
|
285
|
+
|
|
286
|
+
if (overridesPath) {
|
|
287
|
+
const existingOverrides = JSON.parse(fs.readFileSync(overridesPath, 'utf8'));
|
|
288
|
+
const updated = mergeDeprecatedCommands(null, deprecated, existingOverrides);
|
|
289
|
+
fs.writeFileSync(overridesPath, JSON.stringify(updated, null, 2));
|
|
290
|
+
console.log(`\nUpdated overrides file: ${overridesPath}`);
|
|
291
|
+
} else {
|
|
292
|
+
console.log('\nDeprecated commands (JSON):');
|
|
293
|
+
console.log(JSON.stringify(deprecated, null, 2));
|
|
294
|
+
}
|
|
295
|
+
}
|