@redpanda-data/docs-extensions-and-macros 5.10.0 → 5.11.1
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 +105 -43
- package/cli-utils/enterprise-sources.js +244 -0
- package/cli-utils/helm-commented-values.js +361 -0
- package/cli-utils/self-managed-docs-branch.js +32 -24
- package/cli-utils/setup-mcp.js +4 -3
- package/mcp/USER_GUIDE.adoc +1 -1
- package/package.json +2 -1
- package/tools/enterprise-features/parsers.js +0 -14
- package/tools/enterprise-features/verify.js +28 -14
- package/tools/generate-cli-docs.js +1 -0
- package/tools/rpk-docs/generate-rpk-docs.js +62 -0
- package/tools/rpk-docs/generate-x-env-partial.js +284 -0
- package/tools/rpk-docs/rpk-docs-handler.js +11 -21
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Generates the rpk env vars partial (the `-X` option -> RPK_* environment
|
|
5
|
+
* variable mapping table) from rpk itself, so the table cannot drift from
|
|
6
|
+
* the CLI.
|
|
7
|
+
*
|
|
8
|
+
* Data source, in order of preference:
|
|
9
|
+
* 1. The `x_options` array in `rpk --print-tree` JSON (added in
|
|
10
|
+
* redpanda#31520): structured, versioned with the release, and carries
|
|
11
|
+
* descriptions.
|
|
12
|
+
* 2. `rpk -X list` text output, for rpk versions that predate x_options.
|
|
13
|
+
*
|
|
14
|
+
* The partial is included by both reference:rpk/rpk-x-options.adoc and
|
|
15
|
+
* reference:environment-variables.adoc in the docs repo. Hidden -X options
|
|
16
|
+
* (for example cloud_environment, whose values are deliberately
|
|
17
|
+
* undocumented) appear in neither source, so they are excluded
|
|
18
|
+
* automatically.
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
const fs = require('fs')
|
|
22
|
+
const os = require('os')
|
|
23
|
+
const path = require('path')
|
|
24
|
+
const { spawnSync } = require('child_process')
|
|
25
|
+
|
|
26
|
+
// A healthy rpk exposes ~30 -X options. Refuse to overwrite the partial
|
|
27
|
+
// with a suspiciously small table (for example, if -X list output ever
|
|
28
|
+
// changes format and only partially parses).
|
|
29
|
+
const MIN_OPTIONS = 20
|
|
30
|
+
|
|
31
|
+
const GENERATED_BANNER = `// tag::generated[]
|
|
32
|
+
// Generated from rpk's own -X option data. Do not edit manually:
|
|
33
|
+
// regenerate with doc-tools generate rpk-docs (which rewrites this
|
|
34
|
+
// file alongside the rpk command pages) or, for a targeted refresh,
|
|
35
|
+
// doc-tools generate rpk-env-partial.
|
|
36
|
+
`
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Extract -X options from `rpk --print-tree` JSON.
|
|
40
|
+
* @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
|
|
44
|
+
* (the tree's env field is produced by the same derivation rpk reads).
|
|
45
|
+
*/
|
|
46
|
+
function xOptionsFromTree (output) {
|
|
47
|
+
let tree
|
|
48
|
+
try {
|
|
49
|
+
tree = JSON.parse(output)
|
|
50
|
+
} catch {
|
|
51
|
+
return null
|
|
52
|
+
}
|
|
53
|
+
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) }))
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Parse `rpk -X list` output into option keys (fallback for rpk versions
|
|
59
|
+
* whose --print-tree has no x_options).
|
|
60
|
+
* Each line has the form `key=value-hint`; keys contain lowercase
|
|
61
|
+
* letters, digits, dots, and underscores.
|
|
62
|
+
* @param {string} output - Raw stdout from `rpk -X list`
|
|
63
|
+
* @returns {Array<string>} option keys in CLI order
|
|
64
|
+
*/
|
|
65
|
+
function parseXList (output) {
|
|
66
|
+
const keys = []
|
|
67
|
+
for (const line of output.split('\n')) {
|
|
68
|
+
const eq = line.indexOf('=')
|
|
69
|
+
if (eq < 1) continue
|
|
70
|
+
const key = line.slice(0, eq).trim()
|
|
71
|
+
if (/^[a-z0-9._]+$/.test(key)) keys.push(key)
|
|
72
|
+
}
|
|
73
|
+
return keys
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Convert an -X option key to its RPK_* environment variable name:
|
|
78
|
+
* prefix RPK_, uppercase, dots -> underscores.
|
|
79
|
+
* @param {string} key
|
|
80
|
+
* @returns {string}
|
|
81
|
+
*/
|
|
82
|
+
function keyToEnvVar (key) {
|
|
83
|
+
return 'RPK_' + key.toUpperCase().replace(/\./g, '_')
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Convert an -X option key to its section anchor on the rpk-x-options page.
|
|
88
|
+
* The docs repo's AsciiDoc ID convention turns dots into hyphens but keeps
|
|
89
|
+
* underscores (tls.insecure_skip_verify -> tls-insecure_skip_verify),
|
|
90
|
+
* verified against the rendered page IDs. The previous hand-written table
|
|
91
|
+
* hyphenated underscores too, which left 12 of its 31 in-page links dead.
|
|
92
|
+
* @param {string} key
|
|
93
|
+
* @returns {string}
|
|
94
|
+
*/
|
|
95
|
+
function keyToAnchor (key) {
|
|
96
|
+
return key.replace(/\./g, '-')
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Render the partial's AsciiDoc content.
|
|
101
|
+
* @param {Array<{name: string, env: string}>} options - -X options
|
|
102
|
+
* @returns {string}
|
|
103
|
+
*/
|
|
104
|
+
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
|
+
})
|
|
109
|
+
return `${GENERATED_BANNER}
|
|
110
|
+
Every \`-X\` option has a corresponding \`RPK_*\` environment variable. Convert by prefixing with \`RPK_\` and replacing dots with underscores:
|
|
111
|
+
|
|
112
|
+
[cols="1m,1m"]
|
|
113
|
+
|===
|
|
114
|
+
|\`-X\` Option |Environment Variable
|
|
115
|
+
|
|
116
|
+
${rows.join('\n')}
|
|
117
|
+
|===
|
|
118
|
+
// end::generated[]
|
|
119
|
+
`
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Run an rpk invocation from a Go source checkout.
|
|
124
|
+
* @param {string} sourcePath - Path to src/go/rpk in a redpanda checkout
|
|
125
|
+
* @param {Array<string>} args - rpk arguments
|
|
126
|
+
* @returns {string} Raw stdout
|
|
127
|
+
*/
|
|
128
|
+
function runRpkFromSource (sourcePath, args) {
|
|
129
|
+
const result = spawnSync('go', ['run', 'cmd/rpk/main.go', ...args], {
|
|
130
|
+
cwd: sourcePath,
|
|
131
|
+
encoding: 'utf8',
|
|
132
|
+
timeout: 300000, // includes build time
|
|
133
|
+
maxBuffer: 50 * 1024 * 1024
|
|
134
|
+
})
|
|
135
|
+
if (result.status !== 0) {
|
|
136
|
+
throw new Error(`Failed to run rpk ${args.join(' ')} from source: ${result.stderr}`)
|
|
137
|
+
}
|
|
138
|
+
return result.stdout
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Run an rpk invocation with an existing binary.
|
|
143
|
+
* @param {string} rpkBin - Path to the rpk binary
|
|
144
|
+
* @param {Array<string>} args - rpk arguments
|
|
145
|
+
* @returns {string} Raw stdout
|
|
146
|
+
*/
|
|
147
|
+
function runRpkBinary (rpkBin, args) {
|
|
148
|
+
const result = spawnSync(rpkBin, args, {
|
|
149
|
+
encoding: 'utf8',
|
|
150
|
+
timeout: 60000,
|
|
151
|
+
maxBuffer: 50 * 1024 * 1024
|
|
152
|
+
})
|
|
153
|
+
if (result.status !== 0) {
|
|
154
|
+
throw new Error(`Failed to run ${rpkBin} ${args.join(' ')}: ${result.stderr}`)
|
|
155
|
+
}
|
|
156
|
+
return result.stdout
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Resolve a local rpk source path (repo root or src/go/rpk) without
|
|
161
|
+
* touching the checkout.
|
|
162
|
+
* @param {string} p - User-supplied path
|
|
163
|
+
* @returns {string} Path to src/go/rpk
|
|
164
|
+
*/
|
|
165
|
+
function resolveRpkSourcePath (p) {
|
|
166
|
+
const abs = path.resolve(p)
|
|
167
|
+
if (abs.endsWith(path.join('src', 'go', 'rpk'))) return abs
|
|
168
|
+
const nested = path.join(abs, 'src', 'go', 'rpk')
|
|
169
|
+
if (fs.existsSync(nested)) return nested
|
|
170
|
+
throw new Error(`Cannot find rpk source under ${abs}: pass the repo root or the src/go/rpk directory`)
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Find the temp sparse-clone root that prepareSourceFromRef created
|
|
175
|
+
* (os.tmpdir()/rpk-source-<random>/redpanda/src/go/rpk), or null when the path is
|
|
176
|
+
* not one of ours — never delete anything outside the temp dir.
|
|
177
|
+
* @param {string} sourcePath - The src/go/rpk path returned by prepareSourceFromRef
|
|
178
|
+
* @returns {string|null}
|
|
179
|
+
*/
|
|
180
|
+
function tempCloneRoot (sourcePath) {
|
|
181
|
+
let dir = sourcePath
|
|
182
|
+
const tmp = fs.realpathSync(os.tmpdir())
|
|
183
|
+
const real = fs.realpathSync(dir)
|
|
184
|
+
dir = real
|
|
185
|
+
while (dir.startsWith(tmp) && dir !== tmp) {
|
|
186
|
+
if (path.basename(dir).startsWith('rpk-source-') && path.dirname(dir) === tmp) return dir
|
|
187
|
+
dir = path.dirname(dir)
|
|
188
|
+
}
|
|
189
|
+
return null
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Generate the partial and write it to disk.
|
|
194
|
+
* @param {Object} options
|
|
195
|
+
* @param {string} [options.ref] - Git ref of redpanda to build rpk from
|
|
196
|
+
* @param {string} [options.fromSource] - Local src/go/rpk path (skips clone)
|
|
197
|
+
* @param {string} [options.rpkBin] - Existing rpk binary (skips build)
|
|
198
|
+
* @param {string} [options.fromJson] - Versioned tree snapshot from docs-data
|
|
199
|
+
* (skips clone and build; the snapshot must carry x_options)
|
|
200
|
+
* @param {string} options.output - Path to write the partial to
|
|
201
|
+
* @returns {{keyCount: number, output: string, source: string}}
|
|
202
|
+
*/
|
|
203
|
+
function handleXEnvPartialGeneration (options) {
|
|
204
|
+
const { ref, fromSource, rpkBin, fromJson, output } = options
|
|
205
|
+
if (!output) throw new Error('Missing required --output path')
|
|
206
|
+
|
|
207
|
+
let xopts
|
|
208
|
+
let source
|
|
209
|
+
if (fromJson) {
|
|
210
|
+
// Snapshots saved by the rpk-docs handler wrap the tree in raw_tree/tree;
|
|
211
|
+
// accept a bare tree too. There is no rpk to run in this mode, so a
|
|
212
|
+
// snapshot from before x_options existed (redpanda#31520) cannot be used.
|
|
213
|
+
const j = JSON.parse(fs.readFileSync(fromJson, 'utf8'))
|
|
214
|
+
for (const candidate of [j.raw_tree, j.tree, j]) {
|
|
215
|
+
if (!candidate) continue
|
|
216
|
+
xopts = xOptionsFromTree(JSON.stringify(candidate))
|
|
217
|
+
if (xopts) break
|
|
218
|
+
}
|
|
219
|
+
if (!xopts) {
|
|
220
|
+
throw new Error(
|
|
221
|
+
`${fromJson} has no x_options; the snapshot predates redpanda#31520. ` +
|
|
222
|
+
'Regenerate with --ref/--from-source/--rpk-bin instead.'
|
|
223
|
+
)
|
|
224
|
+
}
|
|
225
|
+
source = `snapshot ${path.basename(fromJson)}`
|
|
226
|
+
} else {
|
|
227
|
+
let run
|
|
228
|
+
let cleanupDir = null
|
|
229
|
+
if (rpkBin) {
|
|
230
|
+
run = args => runRpkBinary(rpkBin, args)
|
|
231
|
+
} else if (fromSource && !ref) {
|
|
232
|
+
// Local-source mode with no explicit ref: use the checkout exactly as
|
|
233
|
+
// it stands. prepareSourceFromRef would fetch and check out a ref,
|
|
234
|
+
// silently moving the user's working tree.
|
|
235
|
+
const sourcePath = resolveRpkSourcePath(fromSource)
|
|
236
|
+
run = args => runRpkFromSource(sourcePath, args)
|
|
237
|
+
} else {
|
|
238
|
+
const { prepareSourceFromRef } = require('./rpk-docs-handler.js')
|
|
239
|
+
const sourcePath = prepareSourceFromRef(ref || 'dev', fromSource || null)
|
|
240
|
+
if (!fromSource) {
|
|
241
|
+
// prepareSourceFromRef sparse-cloned into a temp dir this run owns;
|
|
242
|
+
// remove it when done so repeated runs don't accumulate clones.
|
|
243
|
+
cleanupDir = tempCloneRoot(sourcePath)
|
|
244
|
+
}
|
|
245
|
+
run = args => runRpkFromSource(sourcePath, args)
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
try {
|
|
249
|
+
// Preferred: structured x_options from the command tree JSON.
|
|
250
|
+
xopts = xOptionsFromTree(run(['--print-tree']))
|
|
251
|
+
source = 'print-tree x_options'
|
|
252
|
+
if (!xopts) {
|
|
253
|
+
// Fallback for rpk versions that predate x_options in --print-tree.
|
|
254
|
+
// The env name is derived locally with the documented mapping rule.
|
|
255
|
+
xopts = parseXList(run(['-X', 'list'])).map(name => ({ name, env: keyToEnvVar(name) }))
|
|
256
|
+
source = '-X list (fallback)'
|
|
257
|
+
}
|
|
258
|
+
} finally {
|
|
259
|
+
if (cleanupDir) {
|
|
260
|
+
fs.rmSync(cleanupDir, { recursive: true, force: true })
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
if (xopts.length < MIN_OPTIONS) {
|
|
266
|
+
throw new Error(
|
|
267
|
+
`Parsed only ${xopts.length} -X options from ${source} (expected at least ${MIN_OPTIONS}); ` +
|
|
268
|
+
'refusing to write a suspiciously small partial. The output format may have changed.'
|
|
269
|
+
)
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
fs.mkdirSync(path.dirname(output), { recursive: true })
|
|
273
|
+
fs.writeFileSync(output, renderPartial(xopts))
|
|
274
|
+
return { keyCount: xopts.length, output, source }
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
module.exports = {
|
|
278
|
+
xOptionsFromTree,
|
|
279
|
+
parseXList,
|
|
280
|
+
keyToEnvVar,
|
|
281
|
+
keyToAnchor,
|
|
282
|
+
renderPartial,
|
|
283
|
+
handleXEnvPartialGeneration
|
|
284
|
+
}
|
|
@@ -7,7 +7,7 @@ const fs = require('fs')
|
|
|
7
7
|
const os = require('os')
|
|
8
8
|
const semver = require('semver')
|
|
9
9
|
const { findRepoRoot } = require('../../cli-utils/doc-tools-utils')
|
|
10
|
-
const { generateRpkDocs, applyOverridesToTree, resolveReferences, shouldExcludeCommand, shouldUsePartialDir } = require('./generate-rpk-docs')
|
|
10
|
+
const { generateRpkDocs, applyOverridesToTree, resolveReferences, shouldExcludeCommand, shouldUsePartialDir, derivePartialsDir } = require('./generate-rpk-docs')
|
|
11
11
|
const { detectLinuxOnlyFromSource, warnIfDetectionLooksBroken } = require('./detect-platform-commands')
|
|
12
12
|
const { generateRpkDiff, printDiffReport, generateWhatsNewSection, flattenToMap } = require('./report-delta')
|
|
13
13
|
const { loadAndValidateOverrides, ValidationResult } = require('./validate-overrides')
|
|
@@ -1914,26 +1914,14 @@ async function handleRpkDocsGeneration(options = {}) {
|
|
|
1914
1914
|
const defaultOutputDir = path.join(repoRoot, 'modules', 'reference', 'pages', 'rpk')
|
|
1915
1915
|
const finalOutputDir = outputDir || defaultOutputDir
|
|
1916
1916
|
|
|
1917
|
-
//
|
|
1918
|
-
//
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
const pagesIndex = normalizedOutput.lastIndexOf('/pages/')
|
|
1926
|
-
const referenceDir = normalizedOutput.substring(0, pagesIndex)
|
|
1927
|
-
defaultCloudSecretDir = path.join(referenceDir, 'partials')
|
|
1928
|
-
} else {
|
|
1929
|
-
// Arbitrary output directory - use sibling partials directory
|
|
1930
|
-
const outputParent = path.dirname(finalOutputDir)
|
|
1931
|
-
defaultCloudSecretDir = path.join(outputParent, 'partials')
|
|
1932
|
-
}
|
|
1933
|
-
} else {
|
|
1934
|
-
defaultCloudSecretDir = path.join(repoRoot, 'modules', 'reference', 'partials')
|
|
1935
|
-
}
|
|
1936
|
-
const finalCloudSecretDir = cloudSecretDir || defaultCloudSecretDir
|
|
1917
|
+
// Partials live relative to outputDir, not repoRoot: for the docs layout
|
|
1918
|
+
// (.../modules/reference/pages/rpk) that is .../modules/reference/partials,
|
|
1919
|
+
// and for an arbitrary --output-dir it is a partials dir beside the output.
|
|
1920
|
+
// derivePartialsDir owns that rule so each consumer does not re-derive it.
|
|
1921
|
+
const derivedPartialsDir = outputDir
|
|
1922
|
+
? derivePartialsDir(finalOutputDir)
|
|
1923
|
+
: path.join(repoRoot, 'modules', 'reference', 'partials')
|
|
1924
|
+
const finalCloudSecretDir = cloudSecretDir || derivedPartialsDir
|
|
1937
1925
|
|
|
1938
1926
|
// nav.adoc lives at modules/ROOT/nav.adoc relative to the repo root
|
|
1939
1927
|
const navFile = path.join(repoRoot, 'modules', 'ROOT', 'nav.adoc')
|
|
@@ -2139,6 +2127,7 @@ async function handleRpkDocsGeneration(options = {}) {
|
|
|
2139
2127
|
overrides: overridesData,
|
|
2140
2128
|
outputDir: finalOutputDir,
|
|
2141
2129
|
cloudSecretDir: finalCloudSecretDir,
|
|
2130
|
+
envPartialDir: derivedPartialsDir,
|
|
2142
2131
|
rpkVersion,
|
|
2143
2132
|
pluginVersions,
|
|
2144
2133
|
draftMissing,
|
|
@@ -2526,6 +2515,7 @@ async function handleRpkDocsGeneration(options = {}) {
|
|
|
2526
2515
|
overrides: overridesData,
|
|
2527
2516
|
outputDir: finalOutputDir,
|
|
2528
2517
|
cloudSecretDir: finalCloudSecretDir,
|
|
2518
|
+
envPartialDir: derivedPartialsDir,
|
|
2529
2519
|
rpkVersion,
|
|
2530
2520
|
pluginVersions,
|
|
2531
2521
|
draftMissing,
|