@redpanda-data/docs-extensions-and-macros 5.1.3 → 5.2.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 +33 -0
- package/bin/mcp-tools/metrics-docs.js +3 -0
- package/bin/mcp-tools/property-docs.js +3 -0
- package/bin/mcp-tools/rpk-docs.js +6 -0
- package/package.json +1 -1
- package/tools/generate-cli-docs.js +2 -1
- package/tools/redpanda-connect/generate-rpcn-connector-docs.js +51 -0
- package/tools/redpanda-connect/helpers/descriptionWithMetadataInclude.js +15 -0
- package/tools/redpanda-connect/helpers/index.js +2 -0
- package/tools/redpanda-connect/helpers/renderConnectMetadata.js +13 -0
- package/tools/redpanda-connect/metadata-utils.js +112 -0
- package/tools/redpanda-connect/migrate-metadata-to-partials.js +103 -0
- package/tools/redpanda-connect/rpcn-connector-docs-handler.js +2 -0
- package/tools/redpanda-connect/templates/intro.hbs +1 -1
- package/tools/redpanda-connect/templates/metadata-partials.hbs +5 -0
- package/tools/rpk-docs/generate-rpk-docs.js +9 -4
- package/tools/rpk-docs/rpk-docs-handler.js +8 -1
package/bin/doc-tools.js
CHANGED
|
@@ -770,6 +770,7 @@ automation
|
|
|
770
770
|
.option('--template-intro <path>', 'Intro section partial template', path.resolve(__dirname, '../tools/redpanda-connect/templates/intro.hbs'))
|
|
771
771
|
.option('--template-fields <path>', 'Fields section partial template', path.resolve(__dirname, '../tools/redpanda-connect/templates/fields-partials.hbs'))
|
|
772
772
|
.option('--template-examples <path>', 'Examples section partial template', path.resolve(__dirname, '../tools/redpanda-connect/templates/examples-partials.hbs'))
|
|
773
|
+
.option('--template-metadata <path>', 'Metadata section partial template', path.resolve(__dirname, '../tools/redpanda-connect/templates/metadata-partials.hbs'))
|
|
773
774
|
.option('--template-bloblang <path>', 'Custom Handlebars template for bloblang function/method partials')
|
|
774
775
|
.option('--overrides <path>', 'Optional JSON file with overrides', 'docs-data/overrides.json')
|
|
775
776
|
.option('--include-bloblang', 'Include Bloblang functions and methods in generation')
|
|
@@ -792,6 +793,38 @@ automation
|
|
|
792
793
|
await handleRpcnConnectorDocs(options)
|
|
793
794
|
})
|
|
794
795
|
|
|
796
|
+
/**
|
|
797
|
+
* generate migrate-rpcn-metadata
|
|
798
|
+
*
|
|
799
|
+
* @description
|
|
800
|
+
* One-time migration that moves inline `== Metadata` blocks out of connector
|
|
801
|
+
* reference pages (modules/components/pages/<type>/<name>.adoc) and into
|
|
802
|
+
* regenerated partials (modules/components/partials/metadata/<type>/<name>.adoc),
|
|
803
|
+
* replacing the inline block in the page with an include directive. Afterwards
|
|
804
|
+
* `generate rpcn-connector-docs` keeps the partial in sync with the connector's
|
|
805
|
+
* upstream description on every run.
|
|
806
|
+
*
|
|
807
|
+
* @why
|
|
808
|
+
* Metadata documented inline in the main page is never refreshed by normal
|
|
809
|
+
* regeneration, so it drifts from the source. Extracting it into a partial makes
|
|
810
|
+
* it flow through automatically, matching the fields and examples partials.
|
|
811
|
+
*
|
|
812
|
+
* @example
|
|
813
|
+
* # Dry run (default): report the pages that would change
|
|
814
|
+
* npx doc-tools generate migrate-rpcn-metadata
|
|
815
|
+
*
|
|
816
|
+
* # Apply the migration
|
|
817
|
+
* npx doc-tools generate migrate-rpcn-metadata --write
|
|
818
|
+
*/
|
|
819
|
+
automation
|
|
820
|
+
.command('migrate-rpcn-metadata')
|
|
821
|
+
.description('One-time migration of inline connector == Metadata blocks into regenerated partials. Dry run unless --write is given.')
|
|
822
|
+
.option('--write', 'Apply changes (default is a dry run that only reports)')
|
|
823
|
+
.action((options) => {
|
|
824
|
+
const { migrateMetadataToPartials } = require('../tools/redpanda-connect/migrate-metadata-to-partials.js')
|
|
825
|
+
migrateMetadataToPartials({ write: Boolean(options.write) })
|
|
826
|
+
})
|
|
827
|
+
|
|
795
828
|
/**
|
|
796
829
|
* generate property-docs
|
|
797
830
|
*
|
|
@@ -133,6 +133,9 @@ function generateMetricsDocs(args) {
|
|
|
133
133
|
return {
|
|
134
134
|
success: false,
|
|
135
135
|
error: err.message,
|
|
136
|
+
// Report the resolved ref even on failure so callers can confirm
|
|
137
|
+
// parameter defaulting worked independently of the build outcome.
|
|
138
|
+
[refType]: version,
|
|
136
139
|
stdout: err.stdout || '',
|
|
137
140
|
stderr: err.stderr || '',
|
|
138
141
|
exitCode: err.status,
|
|
@@ -162,6 +162,9 @@ function generatePropertyDocs(args) {
|
|
|
162
162
|
return {
|
|
163
163
|
success: false,
|
|
164
164
|
error: err.message,
|
|
165
|
+
// Report the resolved ref even on failure so callers can confirm
|
|
166
|
+
// parameter defaulting worked independently of the build outcome.
|
|
167
|
+
[refType]: version,
|
|
165
168
|
stdout: err.stdout || '',
|
|
166
169
|
stderr: err.stderr || '',
|
|
167
170
|
exitCode: err.status,
|
|
@@ -188,6 +188,12 @@ function generateRpkDocs(args) {
|
|
|
188
188
|
const response = {
|
|
189
189
|
success: false,
|
|
190
190
|
error: err.message,
|
|
191
|
+
// Report the ref that was resolved and attempted, even on failure, so
|
|
192
|
+
// callers can confirm parameter defaulting worked independently of the
|
|
193
|
+
// (environment-dependent) build outcome.
|
|
194
|
+
ref: version,
|
|
195
|
+
version: version,
|
|
196
|
+
ref_type: refType,
|
|
191
197
|
stdout: err.stdout || '',
|
|
192
198
|
stderr: err.stderr || '',
|
|
193
199
|
exitCode: err.status
|
package/package.json
CHANGED
|
@@ -304,7 +304,8 @@ IMPORTANT: This documentation is auto-generated. Do not edit manually. Run \`npm
|
|
|
304
304
|
'cloud-regions',
|
|
305
305
|
'crd-spec',
|
|
306
306
|
'bundle-openapi',
|
|
307
|
-
'update-connect-version'
|
|
307
|
+
'update-connect-version',
|
|
308
|
+
'migrate-rpcn-metadata'
|
|
308
309
|
];
|
|
309
310
|
|
|
310
311
|
generateSubcommands.forEach(subcmd => {
|
|
@@ -18,6 +18,17 @@ Object.entries(helpers).forEach(([name, fn]) => {
|
|
|
18
18
|
// Default “main” template (connector.hbs) which invokes partials {{> intro}}, {{> fields}}, {{> examples}}
|
|
19
19
|
const DEFAULT_TEMPLATE = path.resolve(__dirname, './templates/connector.hbs');
|
|
20
20
|
|
|
21
|
+
// Default template for the regenerated per-connector metadata partial.
|
|
22
|
+
const DEFAULT_METADATA_TEMPLATE = path.resolve(__dirname, './templates/metadata-partials.hbs');
|
|
23
|
+
|
|
24
|
+
// Banner-only content written to a metadata partial when its `== Metadata`
|
|
25
|
+
// section is removed upstream. Kept as an AsciiDoc comment so the file renders
|
|
26
|
+
// nothing while any lingering include directive still resolves cleanly.
|
|
27
|
+
const STALE_METADATA_PARTIAL =
|
|
28
|
+
'// This content is autogenerated. Do not edit manually. ' +
|
|
29
|
+
'The connector description no longer contains a `== Metadata` section, so this partial is intentionally empty. ' +
|
|
30
|
+
'You can remove the include directive that points at this file from the connector page.\n';
|
|
31
|
+
|
|
21
32
|
/**
|
|
22
33
|
* Reads a file at `filePath` and registers it as a Handlebars partial called `name`.
|
|
23
34
|
* Throws if the file cannot be read.
|
|
@@ -241,6 +252,7 @@ async function generateRpcnConnectorDocs(options) {
|
|
|
241
252
|
templateIntro,
|
|
242
253
|
templateFields,
|
|
243
254
|
templateExamples,
|
|
255
|
+
templateMetadata,
|
|
244
256
|
templateBloblang,
|
|
245
257
|
writeFullDrafts,
|
|
246
258
|
cgoOnly = [], // Array of cgo-only connectors from cgo binary inspection
|
|
@@ -336,6 +348,12 @@ async function generateRpcnConnectorDocs(options) {
|
|
|
336
348
|
registerPartial('examples', examplesTemplatePath);
|
|
337
349
|
}
|
|
338
350
|
|
|
351
|
+
// Always register the metadata partial. Like fields and examples, it is
|
|
352
|
+
// regenerated on every run so that metadata fields added to a connector's
|
|
353
|
+
// upstream description flow into the docs automatically.
|
|
354
|
+
const metadataTemplatePath = templateMetadata || DEFAULT_METADATA_TEMPLATE;
|
|
355
|
+
registerPartial('metadata', metadataTemplatePath);
|
|
356
|
+
|
|
339
357
|
// In draft mode, also register the intro partial
|
|
340
358
|
if (writeFullDrafts && templateIntro) {
|
|
341
359
|
registerPartial('intro', templateIntro);
|
|
@@ -344,6 +362,7 @@ async function generateRpcnConnectorDocs(options) {
|
|
|
344
362
|
const outputRoot = path.resolve(process.cwd(), 'modules/components/partials');
|
|
345
363
|
const fieldsOutRoot = path.join(outputRoot, 'fields');
|
|
346
364
|
const examplesOutRoot = path.join(outputRoot, 'examples');
|
|
365
|
+
const metadataOutRoot = path.join(outputRoot, 'metadata');
|
|
347
366
|
// Drafts go to pages/, not partials/
|
|
348
367
|
const componentsRoot = writeFullDrafts
|
|
349
368
|
? path.resolve(process.cwd(), 'modules/components/pages')
|
|
@@ -353,6 +372,7 @@ async function generateRpcnConnectorDocs(options) {
|
|
|
353
372
|
if (!writeFullDrafts) {
|
|
354
373
|
fs.mkdirSync(fieldsOutRoot, { recursive: true });
|
|
355
374
|
fs.mkdirSync(examplesOutRoot, { recursive: true });
|
|
375
|
+
fs.mkdirSync(metadataOutRoot, { recursive: true });
|
|
356
376
|
}
|
|
357
377
|
|
|
358
378
|
let partialsWritten = 0;
|
|
@@ -403,6 +423,37 @@ async function generateRpcnConnectorDocs(options) {
|
|
|
403
423
|
}
|
|
404
424
|
}
|
|
405
425
|
|
|
426
|
+
// Render the metadata partial from the connector's description. Only
|
|
427
|
+
// written when the description contains a `== Metadata` section.
|
|
428
|
+
const metadataOut = handlebars
|
|
429
|
+
.compile('{{> metadata description=description type=type typeDir=typeDir name=name}}')(item);
|
|
430
|
+
|
|
431
|
+
if (type !== 'bloblang-functions' && type !== 'bloblang-methods') {
|
|
432
|
+
// Write under typeDir (not the raw data key) so the file path always
|
|
433
|
+
// matches the include directive built by metadataIncludeLine().
|
|
434
|
+
const mPath = path.join(metadataOutRoot, typeDir, `${name}.adoc`);
|
|
435
|
+
if (metadataOut.trim()) {
|
|
436
|
+
fs.mkdirSync(path.dirname(mPath), { recursive: true });
|
|
437
|
+
fs.writeFileSync(mPath, metadataOut);
|
|
438
|
+
if (!writeFullDrafts) {
|
|
439
|
+
partialsWritten++;
|
|
440
|
+
partialFiles.push(path.relative(process.cwd(), mPath));
|
|
441
|
+
}
|
|
442
|
+
} else if (fs.existsSync(mPath)) {
|
|
443
|
+
// The upstream description dropped its `== Metadata` section, but a
|
|
444
|
+
// previously generated partial is still on disk and an already
|
|
445
|
+
// published page may include it. Deleting the file would break that
|
|
446
|
+
// include (unresolved include directive + build warning), so blank it
|
|
447
|
+
// to a banner-only file: the include still resolves and renders
|
|
448
|
+
// nothing. Log it so a human knows to remove the include line.
|
|
449
|
+
fs.writeFileSync(mPath, STALE_METADATA_PARTIAL);
|
|
450
|
+
console.log(
|
|
451
|
+
`Metadata section removed upstream; emptied stale partial ${path.relative(process.cwd(), mPath)} ` +
|
|
452
|
+
`(remove its include directive from the ${type}/${name} page)`
|
|
453
|
+
);
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
|
|
406
457
|
if (writeFullDrafts) {
|
|
407
458
|
if (String(item.status || '').toLowerCase() === 'deprecated') {
|
|
408
459
|
console.log(`Skipping draft for deprecated component: ${type}/${name}`);
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { descriptionWithMetadataInclude } = require('../metadata-utils.js');
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Handlebars helper: render a connector description with its inline
|
|
7
|
+
* `== Metadata` block replaced by an include of the regenerated metadata
|
|
8
|
+
* partial, so newly drafted pages pick up metadata changes automatically.
|
|
9
|
+
* Falls back to the unchanged description when no metadata section exists.
|
|
10
|
+
* @param {object} item connector data (needs description, type/typeDir, name)
|
|
11
|
+
* @returns {string}
|
|
12
|
+
*/
|
|
13
|
+
module.exports = function descriptionWithMetadataIncludeHelper (item) {
|
|
14
|
+
return descriptionWithMetadataInclude(item);
|
|
15
|
+
};
|
|
@@ -11,6 +11,8 @@ module.exports = {
|
|
|
11
11
|
renderYamlList: require('./renderYamlList.js'),
|
|
12
12
|
renderConnectFields: require('./renderConnectFields.js'),
|
|
13
13
|
renderConnectExamples: require('./renderConnectExamples.js'),
|
|
14
|
+
renderConnectMetadata: require('./renderConnectMetadata.js'),
|
|
15
|
+
descriptionWithMetadataInclude: require('./descriptionWithMetadataInclude.js'),
|
|
14
16
|
renderLeafField: require('./renderLeafField.js'),
|
|
15
17
|
renderObjectField: require('./renderObjectField.js'),
|
|
16
18
|
buildConfigYaml: require('./buildConfigYaml.js'),
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { extractMetadata } = require('../metadata-utils.js');
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Handlebars helper: return the `== Metadata` block extracted from a connector
|
|
7
|
+
* description, or '' when there is none. Used by the metadata partial template.
|
|
8
|
+
* @param {string} description
|
|
9
|
+
* @returns {string}
|
|
10
|
+
*/
|
|
11
|
+
module.exports = function renderConnectMetadata (description) {
|
|
12
|
+
return extractMetadata(description);
|
|
13
|
+
};
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Utilities for extracting the "== Metadata" section out of a connector's
|
|
5
|
+
* `description` prose so it can be emitted as a regenerated partial (like the
|
|
6
|
+
* fields and examples partials) instead of being frozen into a hand-maintained
|
|
7
|
+
* main page.
|
|
8
|
+
*
|
|
9
|
+
* Convention: the connector `.Description()` in the Connect source contains a
|
|
10
|
+
* level-2 AsciiDoc heading `== Metadata` followed by a bullet list, terminated
|
|
11
|
+
* by the next level-2 heading (for example `== Permissions`) or end of string.
|
|
12
|
+
* As of Connect 4.99.0, 52 of the 65 metadata-documenting components already
|
|
13
|
+
* follow this exact heading convention.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
const METADATA_HEADING = /^==\s+Metadata\s*$/;
|
|
17
|
+
const LEVEL2_HEADING = /^==\s+\S/;
|
|
18
|
+
// AsciiDoc listing/literal block delimiter (`----`, possibly longer). Lines
|
|
19
|
+
// inside such blocks must not be treated as headings.
|
|
20
|
+
const BLOCK_DELIMITER = /^-{4,}$/;
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Locate the `== Metadata` section within a description.
|
|
24
|
+
* @param {string} description
|
|
25
|
+
* @returns {{start:number, end:number, block:string}|null} character offsets of
|
|
26
|
+
* the section (heading through the last content line, trailing blank lines
|
|
27
|
+
* excluded) and the extracted block text, or null when no section is present.
|
|
28
|
+
*/
|
|
29
|
+
function locateMetadata (description) {
|
|
30
|
+
if (!description || typeof description !== 'string') return null;
|
|
31
|
+
|
|
32
|
+
const lines = description.split('\n');
|
|
33
|
+
let headingLine = -1;
|
|
34
|
+
let inBlock = false;
|
|
35
|
+
for (let i = 0; i < lines.length; i++) {
|
|
36
|
+
if (BLOCK_DELIMITER.test(lines[i])) { inBlock = !inBlock; continue; }
|
|
37
|
+
if (!inBlock && METADATA_HEADING.test(lines[i])) { headingLine = i; break; }
|
|
38
|
+
}
|
|
39
|
+
if (headingLine === -1) return null;
|
|
40
|
+
|
|
41
|
+
// Find the terminating heading (next level-2 heading after the metadata one).
|
|
42
|
+
let endLine = lines.length;
|
|
43
|
+
inBlock = false;
|
|
44
|
+
for (let i = headingLine + 1; i < lines.length; i++) {
|
|
45
|
+
if (BLOCK_DELIMITER.test(lines[i])) { inBlock = !inBlock; continue; }
|
|
46
|
+
if (!inBlock && LEVEL2_HEADING.test(lines[i])) { endLine = i; break; }
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Trim trailing blank lines inside the section so the block ends cleanly.
|
|
50
|
+
let lastContent = endLine - 1;
|
|
51
|
+
while (lastContent > headingLine && lines[lastContent].trim() === '') lastContent--;
|
|
52
|
+
|
|
53
|
+
const startOffset = lines.slice(0, headingLine).join('\n').length + (headingLine > 0 ? 1 : 0);
|
|
54
|
+
const block = lines.slice(headingLine, lastContent + 1).join('\n');
|
|
55
|
+
const endOffset = startOffset + block.length;
|
|
56
|
+
|
|
57
|
+
return { start: startOffset, end: endOffset, block };
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Return the extracted `== Metadata` block, or '' when there is none.
|
|
62
|
+
* @param {string} description
|
|
63
|
+
* @returns {string}
|
|
64
|
+
*/
|
|
65
|
+
function extractMetadata (description) {
|
|
66
|
+
const found = locateMetadata(description);
|
|
67
|
+
return found ? found.block : '';
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Derive the plural type directory (for example `input` -> `inputs`).
|
|
72
|
+
* @param {object} item connector data with `type` and/or `typeDir`
|
|
73
|
+
* @returns {string}
|
|
74
|
+
*/
|
|
75
|
+
function typeDirFor (item) {
|
|
76
|
+
if (item && item.typeDir) return item.typeDir;
|
|
77
|
+
const type = item && item.type;
|
|
78
|
+
if (!type) return '';
|
|
79
|
+
return type.endsWith('s') ? type : `${type}s`;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Build the Antora include directive for a connector's metadata partial.
|
|
84
|
+
* @param {object} item connector data with `type`/`typeDir` and `name`
|
|
85
|
+
* @returns {string}
|
|
86
|
+
*/
|
|
87
|
+
function metadataIncludeLine (item) {
|
|
88
|
+
return `include::connect:components:partial$metadata/${typeDirFor(item)}/${item.name}.adoc[]`;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Replace the inline `== Metadata` block in a description with an include
|
|
93
|
+
* directive pointing at the regenerated metadata partial, preserving position
|
|
94
|
+
* relative to surrounding sections (such as `== Permissions`). Returns the
|
|
95
|
+
* description unchanged when no metadata section is present.
|
|
96
|
+
* @param {object} item connector data (needs `description`, `type`/`typeDir`, `name`)
|
|
97
|
+
* @returns {string}
|
|
98
|
+
*/
|
|
99
|
+
function descriptionWithMetadataInclude (item) {
|
|
100
|
+
const description = (item && item.description) || '';
|
|
101
|
+
const found = locateMetadata(description);
|
|
102
|
+
if (!found) return description;
|
|
103
|
+
return description.slice(0, found.start) + metadataIncludeLine(item) + description.slice(found.end);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
module.exports = {
|
|
107
|
+
locateMetadata,
|
|
108
|
+
extractMetadata,
|
|
109
|
+
typeDirFor,
|
|
110
|
+
metadataIncludeLine,
|
|
111
|
+
descriptionWithMetadataInclude,
|
|
112
|
+
};
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* One-time migration for the metadata-partial change.
|
|
5
|
+
*
|
|
6
|
+
* Existing connector reference pages carry their `== Metadata` block inline in
|
|
7
|
+
* the main page (`modules/components/pages/<type>/<name>.adoc`), where normal
|
|
8
|
+
* regeneration never refreshes it. This script moves that block into a
|
|
9
|
+
* regenerated partial (`modules/components/partials/metadata/<type>/<name>.adoc`)
|
|
10
|
+
* and replaces the inline block in the page with an include directive, matching
|
|
11
|
+
* the pattern already used for the fields and examples partials.
|
|
12
|
+
*
|
|
13
|
+
* After migration, `doc-tools generate rpcn-connector-docs` keeps the partial in
|
|
14
|
+
* sync with the connector's upstream description on every run.
|
|
15
|
+
*
|
|
16
|
+
* Usage (run from the docs repo root):
|
|
17
|
+
* npx doc-tools generate migrate-rpcn-metadata # dry run, reports changes
|
|
18
|
+
* npx doc-tools generate migrate-rpcn-metadata --write # apply changes
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
const fs = require('fs');
|
|
22
|
+
const path = require('path');
|
|
23
|
+
const { globSync } = require('glob');
|
|
24
|
+
const { locateMetadata, metadataIncludeLine } = require('./metadata-utils.js');
|
|
25
|
+
|
|
26
|
+
const PARTIAL_BANNER =
|
|
27
|
+
'// This content is autogenerated. Do not edit manually. Metadata fields come from the connector\'s description in the Connect source. To change them, update the connector description upstream, or use the doc-tools CLI with the --overrides option: https://redpandadata.atlassian.net/wiki/spaces/DOC/pages/1247543314/Generate+reference+docs+for+Redpanda+Connect';
|
|
28
|
+
|
|
29
|
+
const PAGES_ROOT = path.resolve(process.cwd(), 'modules/components/pages');
|
|
30
|
+
const PARTIALS_ROOT = path.resolve(process.cwd(), 'modules/components/partials/metadata');
|
|
31
|
+
|
|
32
|
+
/** Recursively collect .adoc files under a directory (minimatch glob). */
|
|
33
|
+
function collectAdocFiles (dir) {
|
|
34
|
+
if (!fs.existsSync(dir)) return [];
|
|
35
|
+
return globSync('**/*.adoc', { cwd: dir, absolute: true, nodir: true });
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Migrate inline `== Metadata` blocks in connector pages to regenerated
|
|
40
|
+
* partials plus include directives.
|
|
41
|
+
* @param {object} [options]
|
|
42
|
+
* @param {boolean} [options.write=false] Apply changes (otherwise dry run).
|
|
43
|
+
* @returns {{migrated:number, skipped:number}}
|
|
44
|
+
*/
|
|
45
|
+
function migrateMetadataToPartials ({ write = false } = {}) {
|
|
46
|
+
const pages = collectAdocFiles(PAGES_ROOT);
|
|
47
|
+
|
|
48
|
+
let migrated = 0;
|
|
49
|
+
let skipped = 0;
|
|
50
|
+
|
|
51
|
+
for (const page of pages) {
|
|
52
|
+
// Connector pages live in a <type> subdirectory (pages/<type>/<name>.adoc).
|
|
53
|
+
// Skip pages directly under pages/ — they are not connector pages, and the
|
|
54
|
+
// parent-directory name would be a bogus type.
|
|
55
|
+
if (path.dirname(page) === PAGES_ROOT) { skipped++; continue; }
|
|
56
|
+
|
|
57
|
+
const content = fs.readFileSync(page, 'utf8');
|
|
58
|
+
const found = locateMetadata(content);
|
|
59
|
+
if (!found) { skipped++; continue; }
|
|
60
|
+
|
|
61
|
+
// Derive type directory and connector name from the page path.
|
|
62
|
+
const typeDir = path.basename(path.dirname(page));
|
|
63
|
+
const name = path.basename(page, '.adoc');
|
|
64
|
+
const item = { typeDir, name };
|
|
65
|
+
|
|
66
|
+
// If the block is already an include, nothing to do.
|
|
67
|
+
if (/include::connect:components:partial\$metadata\//.test(found.block)) { skipped++; continue; }
|
|
68
|
+
|
|
69
|
+
const partialPath = path.join(PARTIALS_ROOT, typeDir, `${name}.adoc`);
|
|
70
|
+
const partialContent = `${PARTIAL_BANNER}\n\n${found.block}\n`;
|
|
71
|
+
const newPage = content.slice(0, found.start) + metadataIncludeLine(item) + content.slice(found.end);
|
|
72
|
+
|
|
73
|
+
// Prefer a partial already produced by the generator (source of truth).
|
|
74
|
+
// Only seed one from the page's inline block when none exists yet, so the
|
|
75
|
+
// migration works standalone but never clobbers generated content.
|
|
76
|
+
const partialExists = fs.existsSync(partialPath);
|
|
77
|
+
|
|
78
|
+
console.log(`${write ? 'MIGRATE' : 'WOULD MIGRATE'}: ${typeDir}/${name}`);
|
|
79
|
+
console.log(` -> partial: ${path.relative(process.cwd(), partialPath)}${partialExists ? ' (exists, kept)' : ' (seeded from page)'}`);
|
|
80
|
+
|
|
81
|
+
if (write) {
|
|
82
|
+
if (!partialExists) {
|
|
83
|
+
fs.mkdirSync(path.dirname(partialPath), { recursive: true });
|
|
84
|
+
fs.writeFileSync(partialPath, partialContent);
|
|
85
|
+
}
|
|
86
|
+
fs.writeFileSync(page, newPage);
|
|
87
|
+
}
|
|
88
|
+
migrated++;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
console.log(`\n${write ? 'Migrated' : 'Would migrate'} ${migrated} page(s); ${skipped} without an inline metadata section.`);
|
|
92
|
+
if (!write) console.log('Dry run only. Re-run with --write to apply.');
|
|
93
|
+
|
|
94
|
+
return { migrated, skipped };
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
module.exports = { migrateMetadataToPartials };
|
|
98
|
+
|
|
99
|
+
// Allow direct execution for local development; the supported entry point is
|
|
100
|
+
// `npx doc-tools generate migrate-rpcn-metadata`.
|
|
101
|
+
if (require.main === module) {
|
|
102
|
+
migrateMetadataToPartials({ write: process.argv.includes('--write') });
|
|
103
|
+
}
|
|
@@ -1093,6 +1093,7 @@ async function handleRpcnConnectorDocs (options) {
|
|
|
1093
1093
|
templateIntro: options.templateIntro,
|
|
1094
1094
|
templateFields: options.templateFields,
|
|
1095
1095
|
templateExamples: options.templateExamples,
|
|
1096
|
+
templateMetadata: options.templateMetadata,
|
|
1096
1097
|
templateBloblang: options.templateBloblang,
|
|
1097
1098
|
writeFullDrafts: false,
|
|
1098
1099
|
includeBloblang: !!options.includeBloblang,
|
|
@@ -1882,6 +1883,7 @@ async function handleRpcnConnectorDocs (options) {
|
|
|
1882
1883
|
template: options.templateMain,
|
|
1883
1884
|
templateFields: options.templateFields,
|
|
1884
1885
|
templateExamples: options.templateExamples,
|
|
1886
|
+
templateMetadata: options.templateMetadata,
|
|
1885
1887
|
templateIntro: options.templateIntro,
|
|
1886
1888
|
writeFullDrafts: true,
|
|
1887
1889
|
cgoOnly: binaryAnalysis?.cgoOnly || [],
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
{{#if (renderConnectMetadata description)}}
|
|
2
|
+
// This content is autogenerated. Do not edit manually. Metadata fields come from the connector's description in the Connect source. To change them, update the connector description upstream, or use the doc-tools CLI with the --overrides option: https://redpandadata.atlassian.net/wiki/spaces/DOC/pages/1247543314/Generate+reference+docs+for+Redpanda+Connect
|
|
3
|
+
|
|
4
|
+
{{{renderConnectMetadata description}}}
|
|
5
|
+
{{/if}}
|
|
@@ -1692,13 +1692,18 @@ function formatExamples(text) {
|
|
|
1692
1692
|
i++
|
|
1693
1693
|
} else if (isIndented) {
|
|
1694
1694
|
// Command line — collect all consecutive command lines into one code block
|
|
1695
|
-
// (stops at a blank line or a comment line)
|
|
1696
|
-
|
|
1697
|
-
|
|
1695
|
+
// (stops at a blank line or a comment line). Dedent by the run's minimum
|
|
1696
|
+
// indent rather than trimming each line, so multi-line commands with
|
|
1697
|
+
// backslash continuations keep their relative indentation.
|
|
1698
|
+
const run = []
|
|
1698
1699
|
while (i < lines.length && /^\s{2,}\S/.test(lines[i]) && !lines[i].trim().startsWith('#')) {
|
|
1699
|
-
|
|
1700
|
+
run.push(lines[i])
|
|
1700
1701
|
i++
|
|
1701
1702
|
}
|
|
1703
|
+
const minIndent = Math.min(...run.map(l => l.match(/^\s*/)[0].length))
|
|
1704
|
+
result.push('[,bash]')
|
|
1705
|
+
result.push('----')
|
|
1706
|
+
run.forEach(l => result.push(l.slice(minIndent)))
|
|
1702
1707
|
result.push('----')
|
|
1703
1708
|
} else if (/^\s*`rpk .+`\s*$/.test(line)) {
|
|
1704
1709
|
// Inline backticked command on its own line - convert to code block
|
|
@@ -14,7 +14,7 @@ const { validateDirectory, formatResults } = require('./validate-output')
|
|
|
14
14
|
/**
|
|
15
15
|
* Known rpk plugins that are managed separately (have install/uninstall commands)
|
|
16
16
|
*/
|
|
17
|
-
const KNOWN_PLUGINS = ['ai', 'check', 'connect', 'oxla']
|
|
17
|
+
const KNOWN_PLUGINS = ['ai', 'check', 'connect', 'k8s', 'oxla']
|
|
18
18
|
|
|
19
19
|
/**
|
|
20
20
|
* Parse Go version from 'go version' output
|
|
@@ -497,6 +497,13 @@ function fetchRpkTreeFromLinuxSource(sourcePath) {
|
|
|
497
497
|
} else if (stderr.includes('unknown command') || stderr.includes('Error: unknown command')) {
|
|
498
498
|
console.log(` - ${plugin} is not an installable plugin`)
|
|
499
499
|
} else {
|
|
500
|
+
// A failed install is non-fatal: generation continues, but this
|
|
501
|
+
// plugin's commands will be absent from the tree. Expected for `k8s`
|
|
502
|
+
// during the 26.2 beta window — `rpk k8s install` finds no `latest`
|
|
503
|
+
// release because the plugin publisher's stableVersionRe only promotes
|
|
504
|
+
// pure X.Y.Z versions, so k8s commands only appear at GA. A pre-GA
|
|
505
|
+
// regen that omits `rpk k8s` pages is this, not a generator bug.
|
|
506
|
+
// See redpanda-data/docs#1801.
|
|
500
507
|
console.warn(` ✗ Failed to install ${plugin}: ${stderr || stdout}`)
|
|
501
508
|
}
|
|
502
509
|
}
|