@redpanda-data/docs-extensions-and-macros 5.53.0 → 5.55.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 +4 -5
- package/bin/doc-tools.js +166 -2
- package/bin/mcp-tools/generated-docs-review.js +26 -9
- package/bin/mcp-tools/overrides-audit.js +1 -1
- package/bin/mcp-tools/rpcn-docs.js +4 -14
- package/cli-utils/format-helm-spec.js +4 -3
- package/cli-utils/helm-commented-values.js +88 -18
- package/cli-utils/self-managed-docs-branch.js +36 -30
- package/extension-utils/html-utils.js +27 -0
- package/extensions/REFERENCE.adoc +64 -12
- package/extensions/add-git-dates.js +13 -1
- package/extensions/aggregate-terms.js +34 -20
- package/extensions/generate-rp-connect-info.js +79 -24
- package/extensions/modify-connect-tag-playbook.js +498 -75
- package/extensions/resolve-xrefs-in-attachments.js +1 -12
- package/extensions/url-to-xref.js +3 -0
- package/extensions/util/connect-catalog.js +10 -5
- package/extensions/util/connect-docs-asset.js +188 -0
- package/extensions/util/connect-github.js +16 -0
- package/extensions/version-fetcher/fetch-latest-docker-tag.js +35 -13
- package/extensions/version-fetcher/get-latest-redpanda-helm-version-from-operator.js +64 -40
- package/extensions/version-fetcher/list-connect-releases.js +10 -0
- package/extensions/version-fetcher/release-lines.js +64 -0
- package/extensions/version-fetcher/set-latest-version.js +73 -3
- package/macros/README.adoc +1 -0
- package/macros/REFERENCE.adoc +99 -0
- package/macros/badge.js +5 -8
- package/macros/enterprise.js +2 -5
- package/macros/glossary.js +4 -14
- package/macros/iceberg-explorer.js +242 -0
- package/macros/prop.js +3 -13
- package/mcp/USER_GUIDE.adoc +25 -135
- package/package.json +4 -2
- package/tools/connect-docs/check-build-log.js +311 -0
- package/tools/connect-docs/check-rendered-html.js +469 -0
- package/tools/connect-docs/ci-playbook.yml +77 -0
- package/tools/connect-docs/diff-generated.js +382 -0
- package/tools/connect-docs/pr-comment.js +104 -0
- package/tools/doc-impact-recording/index.js +190 -0
- package/tools/overrides-audit/README.adoc +1 -2
- package/tools/overrides-audit/adapters/properties.js +1 -1
- package/tools/overrides-audit/index.js +3 -4
- package/tools/redpanda-connect/AUTOMATION.md +3 -36
- package/tools/redpanda-connect/README.adoc +5 -33
- package/tools/redpanda-connect/generate-rpcn-connector-docs.js +3 -3
- package/tools/redpanda-connect/pr-summary-formatter.js +6 -4
- package/tools/redpanda-connect/report-delta.js +6 -3
- package/tools/redpanda-connect/rpcn-connector-docs-handler.js +38 -16
- package/tools/redpanda-release-notes/generate-release-notes.js +116 -1
- package/tools/rpk-docs/templates/command.hbs +1 -1
- package/tools/overrides-audit/adapters/connect.js +0 -144
package/bin/doc-tools-mcp.js
CHANGED
|
@@ -10,7 +10,6 @@
|
|
|
10
10
|
* - Context-aware: Works from any repository based on cwd
|
|
11
11
|
* - Antora intelligence: Understands component/module structure
|
|
12
12
|
* - Automation: Run doc-tools generate commands
|
|
13
|
-
* - Auto-discovery: Prompts loaded automatically from mcp/prompts/
|
|
14
13
|
* - Validation: Startup checks ensure all resources are accessible
|
|
15
14
|
* - Telemetry: Usage tracking for adoption metrics
|
|
16
15
|
*/
|
|
@@ -201,7 +200,7 @@ const tools = [
|
|
|
201
200
|
},
|
|
202
201
|
{
|
|
203
202
|
name: 'generate_rpcn_connector_docs',
|
|
204
|
-
description: 'Generate Redpanda Connect connector documentation. Creates component documentation for all connectors. Writers use this when updating connector reference docs.
|
|
203
|
+
description: 'Generate Redpanda Connect connector documentation. Creates component documentation for all connectors. Writers use this when updating connector reference docs. Field, component, and Bloblang descriptions come from the component specs in redpanda-data/connect and redpanda-data/benthos, so fix wrong text with a PR there.',
|
|
205
204
|
inputSchema: {
|
|
206
205
|
type: 'object',
|
|
207
206
|
properties: {
|
|
@@ -239,7 +238,7 @@ const tools = [
|
|
|
239
238
|
},
|
|
240
239
|
overrides: {
|
|
241
240
|
type: 'string',
|
|
242
|
-
description: 'Path to optional JSON file
|
|
241
|
+
description: 'Path to an optional JSON file of description overrides (optional). Nothing is merged unless you pass it.'
|
|
243
242
|
},
|
|
244
243
|
cloud_version: {
|
|
245
244
|
type: 'string',
|
|
@@ -516,7 +515,7 @@ const tools = [
|
|
|
516
515
|
surface: {
|
|
517
516
|
type: 'string',
|
|
518
517
|
description: 'Override surface to audit (optional, defaults to "properties")',
|
|
519
|
-
enum: ['properties', 'rpk'
|
|
518
|
+
enum: ['properties', 'rpk']
|
|
520
519
|
},
|
|
521
520
|
output: {
|
|
522
521
|
type: 'string',
|
|
@@ -594,7 +593,7 @@ const resourceMap = {
|
|
|
594
593
|
|
|
595
594
|
/**
|
|
596
595
|
* Load resource content from team-standards directory
|
|
597
|
-
* @param {string} uri - Resource URI (such as 'redpanda://
|
|
596
|
+
* @param {string} uri - Resource URI (such as 'redpanda://personas')
|
|
598
597
|
* @returns {Object} Resource content
|
|
599
598
|
*/
|
|
600
599
|
function getResourceContent(uri) {
|
package/bin/doc-tools.js
CHANGED
|
@@ -896,7 +896,7 @@ automation
|
|
|
896
896
|
.option('--template-metadata <path>', 'Metadata section partial template', path.resolve(__dirname, '../tools/redpanda-connect/templates/metadata-partials.hbs'))
|
|
897
897
|
.option('--template-description <path>', 'Description section partial template', path.resolve(__dirname, '../tools/redpanda-connect/templates/descriptions-partials.hbs'))
|
|
898
898
|
.option('--template-bloblang <path>', 'Custom Handlebars template for bloblang function/method partials')
|
|
899
|
-
.option('--overrides <path>', 'Optional JSON file
|
|
899
|
+
.option('--overrides <path>', 'Optional JSON file of description overrides to merge into the generated output. Nothing is merged unless you pass it. Connector descriptions come from the connect and benthos component specs, so fix wrong text there')
|
|
900
900
|
.option('--include-bloblang', 'Include Bloblang functions and methods in generation')
|
|
901
901
|
.option('--no-partials', 'Skip the generated partials, config snippets, and Bloblang reference because the connect repo publishes them. What\'s new, diffs, drafts, nav, and the version bump still run')
|
|
902
902
|
.option('--prune-orphaned-descriptions', 'Allow the description-partial orphan sweep to blank more than 10% of the tree. Only use this when the connector dataset is confirmed complete: an incomplete dataset looks identical to a mass upstream deletion and the sweep blanks published content')
|
|
@@ -2881,6 +2881,43 @@ programCli
|
|
|
2881
2881
|
runCli(options)
|
|
2882
2882
|
})
|
|
2883
2883
|
|
|
2884
|
+
/**
|
|
2885
|
+
* doc-impact-recording
|
|
2886
|
+
*
|
|
2887
|
+
* @description
|
|
2888
|
+
* Extracts every redpanda-docs MCP call and its result from a Claude Code
|
|
2889
|
+
* transcript (the claude-code-action execution file, or `claude -p
|
|
2890
|
+
* --output-format stream-json` output) and writes them as a doc-impact eval
|
|
2891
|
+
* recording. Only the tool name, arguments and result content are kept.
|
|
2892
|
+
* Writes nothing when the session made no docs calls.
|
|
2893
|
+
*
|
|
2894
|
+
* @why
|
|
2895
|
+
* The doc-impact eval replays docs answers per PR. Answers recorded later
|
|
2896
|
+
* come from docs that may already contain the writer's fix, so the eval
|
|
2897
|
+
* would score a correct "already documented" as a miss. The doc-strings
|
|
2898
|
+
* review saves what the docs server told it at review time, and the eval
|
|
2899
|
+
* miner turns that into the item's recording.
|
|
2900
|
+
*
|
|
2901
|
+
* @example
|
|
2902
|
+
* # What the doc-strings review runs after its Claude step
|
|
2903
|
+
* npx doc-tools doc-impact-recording --execution-file "$RUNNER_TEMP/claude-execution-output.json" \
|
|
2904
|
+
* --output recording.json --item redpanda-operator-1615 --server-url https://docs.redpanda.com/mcp
|
|
2905
|
+
*/
|
|
2906
|
+
programCli
|
|
2907
|
+
.command('doc-impact-recording')
|
|
2908
|
+
.description('Extract the docs MCP calls from a Claude Code transcript into a doc-impact eval recording')
|
|
2909
|
+
.requiredOption('--execution-file <path>', 'Transcript: a JSON array of SDK messages or stream-json lines')
|
|
2910
|
+
.requiredOption('--output <path>', 'Recording file to write (not written when there are no docs calls)')
|
|
2911
|
+
.option('--item <id>', 'Item id to store in the recording')
|
|
2912
|
+
.option('--server <name>', 'MCP server name in the transcript tool names', 'redpanda-docs')
|
|
2913
|
+
.option('--server-url <url>', 'URL of that MCP server, stored in the recording')
|
|
2914
|
+
.option('--model <name>', 'Model to store (default: the model in the transcript init message)')
|
|
2915
|
+
.option('--recorded-at <iso>', 'Timestamp to store (default: now)')
|
|
2916
|
+
.action((options) => {
|
|
2917
|
+
const { runCli } = require('../tools/doc-impact-recording')
|
|
2918
|
+
process.exit(runCli(options))
|
|
2919
|
+
})
|
|
2920
|
+
|
|
2884
2921
|
/**
|
|
2885
2922
|
* lint-screenshots
|
|
2886
2923
|
*
|
|
@@ -2930,6 +2967,133 @@ programCli
|
|
|
2930
2967
|
runCli(options)
|
|
2931
2968
|
})
|
|
2932
2969
|
|
|
2970
|
+
/**
|
|
2971
|
+
* check-build-log
|
|
2972
|
+
*
|
|
2973
|
+
* @description
|
|
2974
|
+
* Check an Antora JSON log (`antora --log-format json`): exit 1 when any
|
|
2975
|
+
* record is at level error or fatal, and, with --min-pages and --site-dir,
|
|
2976
|
+
* when fewer HTML pages were written than expected. Prints a Markdown
|
|
2977
|
+
* summary (counts by level, then the error and warning messages grouped and
|
|
2978
|
+
* deduplicated, with file and line) for $GITHUB_STEP_SUMMARY. Warnings never
|
|
2979
|
+
* fail the check. With --blocking-sources, only errors from those
|
|
2980
|
+
* repositories (or with no source) fail it, so a broken page in another
|
|
2981
|
+
* content source of the playbook is reported without blocking.
|
|
2982
|
+
*
|
|
2983
|
+
* @why
|
|
2984
|
+
* Antora's --log-failure-level fails the build but explains nothing in a job
|
|
2985
|
+
* summary, and cannot catch a build that exits 0 having written no files,
|
|
2986
|
+
* which is what Antora does on an unsupported Node version. The Connect docs
|
|
2987
|
+
* PR check runs Antora with --log-failure-level=fatal so this command, not
|
|
2988
|
+
* Antora, decides.
|
|
2989
|
+
*
|
|
2990
|
+
* @example
|
|
2991
|
+
* npx antora --log-format json --log-failure-level=fatal antora-playbook.yml > build.ndjson
|
|
2992
|
+
* npx doc-tools check-build-log build.ndjson --site-dir build/site/connect --min-pages 400 >> "$GITHUB_STEP_SUMMARY"
|
|
2993
|
+
*
|
|
2994
|
+
* # Block only on errors from these repositories; list the rest
|
|
2995
|
+
* npx doc-tools check-build-log build.ndjson --blocking-sources redpanda-data/rp-connect-docs,redpanda-data/connect
|
|
2996
|
+
*/
|
|
2997
|
+
programCli
|
|
2998
|
+
.command('check-build-log')
|
|
2999
|
+
.description('Check an Antora JSON build log: fail on error or fatal records or too few pages, print a Markdown summary')
|
|
3000
|
+
.argument('<log>', 'Antora log written with --log-format json (one JSON record per line)')
|
|
3001
|
+
.option('--site-dir <dir>', 'Output directory to count HTML pages in (with --min-pages)')
|
|
3002
|
+
.option('--min-pages <n>', 'Fail when fewer than <n> HTML pages were written under --site-dir')
|
|
3003
|
+
.option('--blocking-sources <repos>', 'Comma-separated owner/name repositories whose errors block (default: all). Errors with no source always block; others are listed only')
|
|
3004
|
+
.option('--format <format>', 'Output format: markdown or json', 'markdown')
|
|
3005
|
+
.option('--output <path>', 'Also write the output to this file')
|
|
3006
|
+
.action((log, options) => {
|
|
3007
|
+
const { runCli } = require('../tools/connect-docs/check-build-log')
|
|
3008
|
+
runCli(log, options)
|
|
3009
|
+
})
|
|
3010
|
+
|
|
3011
|
+
/**
|
|
3012
|
+
* check-rendered-html
|
|
3013
|
+
*
|
|
3014
|
+
* @description
|
|
3015
|
+
* Scan the rendered HTML of one or more Antora component paths for AsciiDoc
|
|
3016
|
+
* that did not convert: literal backticks outside code, literal `xref:` or
|
|
3017
|
+
* `include::` text, leftover {page-*} or {env-*} attribute references, `|===`
|
|
3018
|
+
* table markup, Asciidoctor's "Unresolved include directive" text, links with
|
|
3019
|
+
* the `unresolved` class, and empty sections (a heading followed directly by a
|
|
3020
|
+
* heading of the same or higher level). Also checks links: a fragment with no
|
|
3021
|
+
* matching id on its target page (broken-anchor), and a relative or
|
|
3022
|
+
* root-relative link under a link root that is not in the build
|
|
3023
|
+
* (broken-link). External links are ignored; links outside every link root,
|
|
3024
|
+
* or into a root the build does not contain, are counted as unchecked. Only
|
|
3025
|
+
* the page body is scanned. Reports findings per page and exits 0 unless
|
|
3026
|
+
* --strict.
|
|
3027
|
+
*
|
|
3028
|
+
* With --changed-pages, findings are split into those on the listed pages and
|
|
3029
|
+
* the rest, and --strict exits 1 only when a listed page has findings, so
|
|
3030
|
+
* defects already on the base never fail a PR.
|
|
3031
|
+
*
|
|
3032
|
+
* @why
|
|
3033
|
+
* These defects reach readers without a single line in the Antora log: two
|
|
3034
|
+
* backticks for an empty enum option, or a backtick glued to the next word,
|
|
3035
|
+
* render as literal backticks on the published page.
|
|
3036
|
+
*
|
|
3037
|
+
* @example
|
|
3038
|
+
* npx doc-tools check-rendered-html build/site --component connect >> "$GITHUB_STEP_SUMMARY"
|
|
3039
|
+
*
|
|
3040
|
+
* # Fail on any finding
|
|
3041
|
+
* npx doc-tools check-rendered-html build/site --strict
|
|
3042
|
+
*
|
|
3043
|
+
* # Self-managed and Cloud Connect pages; fail only on the pages a PR changed
|
|
3044
|
+
* npx doc-tools check-rendered-html build/site --component connect,cloud-data-platform/develop/connect \
|
|
3045
|
+
* --changed-pages changed-pages.txt --strict
|
|
3046
|
+
*/
|
|
3047
|
+
programCli
|
|
3048
|
+
.command('check-rendered-html')
|
|
3049
|
+
.description('Scan rendered Antora HTML for unconverted AsciiDoc (literal backticks, xref:, include::, attributes, tables, empty sections) and broken anchors and links')
|
|
3050
|
+
.argument('<site-dir>', 'Antora output directory (the --to-dir of the build)')
|
|
3051
|
+
.option('--component <path>', 'Component output path to scan under <site-dir>; repeat it or give a comma list, for example connect,cloud-data-platform/develop/connect (default: connect)', (value, previous = []) => previous.concat(value))
|
|
3052
|
+
.option('--link-root <path>', 'Site path whose linked pages must exist; repeat it or give a comma list. Scanned components are always included. Links outside every root, or into a root not in the build, are counted as unchecked (default: connect,cloud-data-platform/develop/connect)', (value, previous = []) => previous.concat(value))
|
|
3053
|
+
.option('--pages <file>', 'Only scan the pages listed in <file>, one path relative to <site-dir> per line')
|
|
3054
|
+
.option('--changed-pages <file>', 'Pages a PR changed: one path relative to <site-dir> per line (the sitePath from connect-docs-diff) or connect-docs-diff --format json output. All pages are still scanned; findings are split into changed and other pages, and --strict fails only on changed pages')
|
|
3055
|
+
.option('--format <format>', 'Output format: markdown or json', 'markdown')
|
|
3056
|
+
.option('--output <path>', 'Also write the output to this file')
|
|
3057
|
+
.option('--strict', 'Exit 1 when any finding exists, or with --changed-pages when a changed page has findings (default: always exit 0)')
|
|
3058
|
+
.action((siteDir, options) => {
|
|
3059
|
+
const { runCli } = require('../tools/connect-docs/check-rendered-html')
|
|
3060
|
+
runCli(siteDir, options)
|
|
3061
|
+
})
|
|
3062
|
+
|
|
3063
|
+
/**
|
|
3064
|
+
* connect-docs-diff
|
|
3065
|
+
*
|
|
3066
|
+
* @description
|
|
3067
|
+
* Compare two trees written by connect's docs generator, for example a PR's
|
|
3068
|
+
* merge base and its head, and map each added, removed, or changed partial or
|
|
3069
|
+
* example to the docs.redpanda.com page that includes it. Prints a Markdown
|
|
3070
|
+
* table of changed pages with links, then a unified diff per file in
|
|
3071
|
+
* collapsible blocks, capped in size. A tree root can be connect's docs/
|
|
3072
|
+
* directory, its modules/ directory, or the components/ directory.
|
|
3073
|
+
*
|
|
3074
|
+
* @why
|
|
3075
|
+
* Writers review Go string literals today and never see the page. The
|
|
3076
|
+
* generated output is the published change: connect strings, benthos strings
|
|
3077
|
+
* from a go.mod bump, and generator changes all show up here.
|
|
3078
|
+
*
|
|
3079
|
+
* @example
|
|
3080
|
+
* npx doc-tools connect-docs-diff base/modules head/modules >> "$GITHUB_STEP_SUMMARY"
|
|
3081
|
+
* npx doc-tools connect-docs-diff base/modules head/modules --format json
|
|
3082
|
+
*/
|
|
3083
|
+
programCli
|
|
3084
|
+
.command('connect-docs-diff')
|
|
3085
|
+
.description('Diff two generated Connect docs trees and map each changed partial or example to its published page')
|
|
3086
|
+
.argument('<base-dir>', 'Generated tree of the base (merge base) commit')
|
|
3087
|
+
.argument('<head-dir>', 'Generated tree of the head commit')
|
|
3088
|
+
.option('--format <format>', 'Output format: markdown or json', 'markdown')
|
|
3089
|
+
.option('--site-url <url>', 'Docs site the page links point at', 'https://docs.redpanda.com')
|
|
3090
|
+
.option('--max-bytes <n>', 'Cap on the Markdown output size; diffs past it are counted, not shown', '60000')
|
|
3091
|
+
.option('--output <path>', 'Also write the output to this file')
|
|
3092
|
+
.action((baseDir, headDir, options) => {
|
|
3093
|
+
const { runCli } = require('../tools/connect-docs/diff-generated')
|
|
3094
|
+
runCli(baseDir, headDir, options)
|
|
3095
|
+
})
|
|
3096
|
+
|
|
2933
3097
|
/**
|
|
2934
3098
|
* preview-string
|
|
2935
3099
|
*
|
|
@@ -3009,7 +3173,7 @@ overridesGroup
|
|
|
3009
3173
|
.description('Classify each override field as redundant, upstreamable, or keep against extracted source strings')
|
|
3010
3174
|
.requiredOption('--overrides <path>', 'Path to the overrides JSON file (for example docs-data/property-overrides.json)')
|
|
3011
3175
|
.option('--extracted <path>', 'Path to the extracted source JSON (property extractor raw output; required for the properties surface)')
|
|
3012
|
-
.addOption(new Option('--surface <surface>', 'Override surface to audit').choices(['properties', 'rpk'
|
|
3176
|
+
.addOption(new Option('--surface <surface>', 'Override surface to audit').choices(['properties', 'rpk']).default('properties'))
|
|
3013
3177
|
.addOption(new Option('--format <format>', 'Output format').choices(['json', 'human']).default('json'))
|
|
3014
3178
|
.option('--repo <path>', 'Redpanda checkout to extract raw source strings from (alternative to --extracted)')
|
|
3015
3179
|
.option('--output <path>', 'Also write the JSON result to this file')
|
|
@@ -189,7 +189,7 @@ function generateReviewReport(results, outputPath) {
|
|
|
189
189
|
report += `. Regenerate docs\n\n`;
|
|
190
190
|
} else if (issue.issue.includes('Invalid $ref')) {
|
|
191
191
|
report += `*Action:*\n\n`;
|
|
192
|
-
report += `. Open
|
|
192
|
+
report += `. Open \`${issue.file}\`\n`;
|
|
193
193
|
report += `. Find the reference at \`${issue.path}\`\n`;
|
|
194
194
|
report += `. Either add the missing definition or fix the reference\n`;
|
|
195
195
|
report += `. Regenerate docs\n\n`;
|
|
@@ -273,6 +273,17 @@ function generateReviewReport(results, outputPath) {
|
|
|
273
273
|
return report;
|
|
274
274
|
}
|
|
275
275
|
|
|
276
|
+
/**
|
|
277
|
+
* Where to fix Redpanda Connect reference text. The connect repo generates
|
|
278
|
+
* the field, component, and Bloblang partials from its specs and the benthos
|
|
279
|
+
* specs, and each Connect release publishes them for the docs build.
|
|
280
|
+
*/
|
|
281
|
+
const RPCN_SOURCE_GUIDANCE =
|
|
282
|
+
'Connector field, component, and Bloblang descriptions are generated from the component specs in ' +
|
|
283
|
+
'redpanda-data/connect and redpanda-data/benthos. To fix wrong or missing text, open a PR against ' +
|
|
284
|
+
'the spec in that repository; the fix ships in the next Connect release. Edit hand-written page ' +
|
|
285
|
+
'prose in rp-connect-docs.';
|
|
286
|
+
|
|
276
287
|
/**
|
|
277
288
|
* Review generated documentation for quality issues
|
|
278
289
|
* @param {Object} args - Arguments
|
|
@@ -282,7 +293,7 @@ function generateReviewReport(results, outputPath) {
|
|
|
282
293
|
* @param {string} [args.properties_dir] - Custom path to properties directory (default: modules/reference)
|
|
283
294
|
* @param {string} [args.metrics_file] - Custom path to metrics file (default: modules/reference/pages/public-metrics-reference.adoc)
|
|
284
295
|
* @param {string} [args.rpk_dir] - Custom path to RPK docs directory (default: autogenerated/{version}/rpk)
|
|
285
|
-
* @param {string} [args.overrides_file] -
|
|
296
|
+
* @param {string} [args.overrides_file] - Optional connector overrides file to validate for RPCN connectors. Without it, the review points at the connect and benthos specs, where connector descriptions live
|
|
286
297
|
* @returns {Object} Review results with issues and suggestions
|
|
287
298
|
*/
|
|
288
299
|
function reviewGeneratedDocs(args) {
|
|
@@ -524,16 +535,21 @@ function reviewGeneratedDocs(args) {
|
|
|
524
535
|
}
|
|
525
536
|
|
|
526
537
|
case 'rpcn_connectors': {
|
|
527
|
-
//
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
538
|
+
// Connector field, component, and Bloblang descriptions are generated
|
|
539
|
+
// from the connect and benthos specs, so there is no overrides file to
|
|
540
|
+
// check by default. Only an overrides file passed explicitly is validated.
|
|
541
|
+
if (!args.overrides_file) {
|
|
542
|
+
suggestions.push(RPCN_SOURCE_GUIDANCE);
|
|
543
|
+
break;
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
const overridesPath = path.join(repoRoot.root, args.overrides_file);
|
|
531
547
|
|
|
532
548
|
if (!fs.existsSync(overridesPath)) {
|
|
533
549
|
return {
|
|
534
550
|
success: false,
|
|
535
|
-
error: `
|
|
536
|
-
suggestion:
|
|
551
|
+
error: `Overrides file not found at ${overridesPath}`,
|
|
552
|
+
suggestion: `Check the overrides_file path, or omit it. ${RPCN_SOURCE_GUIDANCE}`
|
|
537
553
|
};
|
|
538
554
|
}
|
|
539
555
|
|
|
@@ -736,5 +752,6 @@ function reviewGeneratedDocs(args) {
|
|
|
736
752
|
|
|
737
753
|
module.exports = {
|
|
738
754
|
generateReviewReport,
|
|
739
|
-
reviewGeneratedDocs
|
|
755
|
+
reviewGeneratedDocs,
|
|
756
|
+
RPCN_SOURCE_GUIDANCE
|
|
740
757
|
};
|
|
@@ -11,7 +11,7 @@ const { findRepoRoot, getDocToolsCommand, MAX_EXEC_BUFFER_SIZE, DEFAULT_COMMAND_
|
|
|
11
11
|
* @param {string} args.overrides - Path to the overrides JSON file (for example "docs-data/property-overrides.json")
|
|
12
12
|
* @param {string} [args.extracted] - Path to the extracted source JSON (property extractor raw output; required for the properties surface)
|
|
13
13
|
* @param {string} [args.repo] - Redpanda checkout to extract raw source strings from (alternative to args.extracted, properties surface only)
|
|
14
|
-
* @param {string} [args.surface] - Override surface: 'properties'
|
|
14
|
+
* @param {string} [args.surface] - Override surface: 'properties' or 'rpk'
|
|
15
15
|
* @param {string} [args.output] - Also write the JSON result to this file (relative to repo root)
|
|
16
16
|
* @returns {Object} Audit results with manifest and per-class summary
|
|
17
17
|
*/
|
|
@@ -12,8 +12,6 @@ const { execSync, spawnSync } = require('child_process');
|
|
|
12
12
|
const { findRepoRoot, MAX_EXEC_BUFFER_SIZE, DEFAULT_COMMAND_TIMEOUT } = require('./utils');
|
|
13
13
|
const { getAntoraStructure } = require('./antora');
|
|
14
14
|
const cache = require('./cache');
|
|
15
|
-
const fs = require('fs');
|
|
16
|
-
const path = require('path');
|
|
17
15
|
|
|
18
16
|
/**
|
|
19
17
|
* Generate Redpanda Connect connector documentation
|
|
@@ -59,19 +57,11 @@ function generateRpConnectDocs(args = {}) {
|
|
|
59
57
|
cmdArgs.push(args.csv);
|
|
60
58
|
}
|
|
61
59
|
|
|
62
|
-
//
|
|
63
|
-
|
|
64
|
-
if (
|
|
65
|
-
// Try to find docs-data/overrides.json in current directory
|
|
66
|
-
const defaultOverrides = path.join(process.cwd(), 'docs-data', 'overrides.json');
|
|
67
|
-
if (fs.existsSync(defaultOverrides)) {
|
|
68
|
-
overridesPath = defaultOverrides;
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
if (overridesPath) {
|
|
60
|
+
// Overrides are opt-in. Connector descriptions come from the connect and
|
|
61
|
+
// benthos specs, so no file is picked up implicitly.
|
|
62
|
+
if (args.overrides) {
|
|
73
63
|
cmdArgs.push('--overrides');
|
|
74
|
-
cmdArgs.push(
|
|
64
|
+
cmdArgs.push(args.overrides);
|
|
75
65
|
}
|
|
76
66
|
if (args.cloud_version) {
|
|
77
67
|
cmdArgs.push('--cloud-version');
|
|
@@ -9,9 +9,10 @@
|
|
|
9
9
|
*/
|
|
10
10
|
function formatHelmSpec (doc) {
|
|
11
11
|
const formatted = doc
|
|
12
|
-
//
|
|
13
|
-
|
|
14
|
-
.replace(/(\[\d
|
|
12
|
+
// an array index inside a heading's link text (ingress.hosts[0].host)
|
|
13
|
+
// closes the link text early unless its "]" is escaped
|
|
14
|
+
.replace(/(\[\d+)\]\./g, '$1\\].')
|
|
15
|
+
.replace(/(\[\d+)\]\]/g, '$1\\]]')
|
|
15
16
|
// a section title that is a bare URL has to be a link macro to render
|
|
16
17
|
.replace(/^=== +(https?:\/\/[^[]*)\[([^\]]*)\]/gm, '=== link:++$1++[$2]')
|
|
17
18
|
.replace(/^== # (.*)$/gm, '= $1')
|
|
@@ -24,6 +24,19 @@
|
|
|
24
24
|
*
|
|
25
25
|
* yields `external.domain`.
|
|
26
26
|
*
|
|
27
|
+
* The description attaches to the first commented-out key after it. When
|
|
28
|
+
* more commented-out siblings follow in the same comment run, each one
|
|
29
|
+
* with its own plain comment above it is documented by that comment:
|
|
30
|
+
*
|
|
31
|
+
* external:
|
|
32
|
+
* # -- Optional domain advertised to external clients.
|
|
33
|
+
* # domain: local
|
|
34
|
+
* # Optional list of advertised addresses.
|
|
35
|
+
* # addresses:
|
|
36
|
+
*
|
|
37
|
+
* yields `external.domain` and `external.addresses`. A sibling with no
|
|
38
|
+
* comment of its own is not documented.
|
|
39
|
+
*
|
|
27
40
|
* 2. Explicit, fully-qualified syntax that works anywhere in the file:
|
|
28
41
|
*
|
|
29
42
|
* # @doc external.addresses -- Optional list of advertised addresses.
|
|
@@ -89,10 +102,26 @@ function parseValuesFile (yamlText, { attachRealKeys = false } = {}) {
|
|
|
89
102
|
const records = []
|
|
90
103
|
const stack = [] // enclosing real keys: { name, indent }
|
|
91
104
|
|
|
92
|
-
// Accumulating comment block: { descLines, default, candidate }.
|
|
93
|
-
//
|
|
94
|
-
//
|
|
95
|
-
//
|
|
105
|
+
// Accumulating comment block: { marked, descLines, default, candidate }.
|
|
106
|
+
// A marked block opens at a `# --` line. Its description is every comment
|
|
107
|
+
// line up to the first commented-out key, and that key (the candidate) is
|
|
108
|
+
// the one it documents, the same way helm-docs reads a `# --` block as the
|
|
109
|
+
// description of the key directly below it.
|
|
110
|
+
//
|
|
111
|
+
// Optional values often ship as a run of commented-out siblings under one
|
|
112
|
+
// marker, each with its own plain comment above it:
|
|
113
|
+
//
|
|
114
|
+
// # -- Optional domain.
|
|
115
|
+
// # domain: local
|
|
116
|
+
// # Optional list of addresses.
|
|
117
|
+
// # addresses:
|
|
118
|
+
//
|
|
119
|
+
// Once a block has its key, a plain comment line at that key's depth (or
|
|
120
|
+
// shallower) ends it and opens an unmarked block for the next sibling, so
|
|
121
|
+
// `addresses` is documented by its own comment instead of handing its key
|
|
122
|
+
// line to the domain description. An unmarked block is only ever emitted
|
|
123
|
+
// when it has description text and a key; it never becomes a dead marker.
|
|
124
|
+
// The run ends at the first non-comment line, as a marked block does.
|
|
96
125
|
let block = null
|
|
97
126
|
let atDoc = null // accumulating @doc entry: { path, descLines, default }
|
|
98
127
|
let skipScalarIndent = -1 // inside a block scalar when >= 0
|
|
@@ -135,6 +164,7 @@ function parseValuesFile (yamlText, { attachRealKeys = false } = {}) {
|
|
|
135
164
|
// The generator drops these; the linter reports them, because their
|
|
136
165
|
// description silently never ships.
|
|
137
166
|
const emitDead = (b) => {
|
|
167
|
+
if (!b.marked) return
|
|
138
168
|
record({
|
|
139
169
|
kind: 'dead-marker',
|
|
140
170
|
descLines: b.descLines,
|
|
@@ -145,12 +175,26 @@ function parseValuesFile (yamlText, { attachRealKeys = false } = {}) {
|
|
|
145
175
|
})
|
|
146
176
|
}
|
|
147
177
|
|
|
178
|
+
const openUnmarkedBlock = (line, firstDescLine) => ({
|
|
179
|
+
marked: false,
|
|
180
|
+
markerLine: line,
|
|
181
|
+
lastLine: line,
|
|
182
|
+
descLines: firstDescLine === null ? [] : [firstDescLine],
|
|
183
|
+
default: '',
|
|
184
|
+
annotations: {},
|
|
185
|
+
candidate: null,
|
|
186
|
+
})
|
|
187
|
+
|
|
148
188
|
// Emit the active block's candidate, if any, and close the block. The
|
|
149
189
|
// emitted key suppresses its own commented subtree until real content
|
|
150
190
|
// appears, so nested example structures do not emit bogus paths.
|
|
151
191
|
const flushBlock = () => {
|
|
152
192
|
if (!block) return
|
|
153
|
-
if (block.candidate) {
|
|
193
|
+
if (block.candidate && !block.marked && !block.descLines.some((l) => l.trim() !== '')) {
|
|
194
|
+
// An unmarked sibling with no comment of its own has nothing to say;
|
|
195
|
+
// still suppress its commented subtree.
|
|
196
|
+
suppressInnerIndent = block.candidate.effIndent
|
|
197
|
+
} else if (block.candidate) {
|
|
154
198
|
const { name, effIndent } = block.candidate
|
|
155
199
|
const parents = stack.filter((k) => k.indent < effIndent).map((k) => k.name)
|
|
156
200
|
record({
|
|
@@ -233,13 +277,26 @@ function parseValuesFile (yamlText, { attachRealKeys = false } = {}) {
|
|
|
233
277
|
// "key" is a URL scheme and the "value" is the scheme-relative part.
|
|
234
278
|
const simpleValue = (value === '' || !/\s/.test(value)) && !value.startsWith('/')
|
|
235
279
|
if (block && simpleValue) {
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
//
|
|
239
|
-
//
|
|
240
|
-
|
|
280
|
+
const key = { name: commentedKey[3], effIndent }
|
|
281
|
+
if (!block.candidate || effIndent < block.candidate.effIndent) {
|
|
282
|
+
// The first key line after the description is the documented key.
|
|
283
|
+
// A shallower key replaces it, because the deeper line was a nested
|
|
284
|
+
// example structure rather than the key itself.
|
|
285
|
+
block.candidate = key
|
|
286
|
+
block.lastLine = i
|
|
287
|
+
} else if (effIndent > block.candidate.effIndent) {
|
|
288
|
+
// A nested line of the documented key's commented-out example.
|
|
289
|
+
block.lastLine = i
|
|
290
|
+
} else {
|
|
291
|
+
// A sibling key with no comment of its own. The documented key is
|
|
292
|
+
// complete; the sibling opens an unmarked block, which flushBlock
|
|
293
|
+
// drops because it has no description, while still suppressing
|
|
294
|
+
// the sibling's own commented subtree.
|
|
295
|
+
flushBlock()
|
|
296
|
+
suppressInnerIndent = -1
|
|
297
|
+
block = openUnmarkedBlock(i, null)
|
|
298
|
+
block.candidate = key
|
|
241
299
|
}
|
|
242
|
-
block.lastLine = i
|
|
243
300
|
continue
|
|
244
301
|
}
|
|
245
302
|
}
|
|
@@ -248,18 +305,30 @@ function parseValuesFile (yamlText, { attachRealKeys = false } = {}) {
|
|
|
248
305
|
if (descMarker) {
|
|
249
306
|
flushAtDoc()
|
|
250
307
|
flushBlock()
|
|
251
|
-
block = { markerLine: i, lastLine: i, descLines: [descMarker[1]], default: '', annotations: {}, candidate: null }
|
|
308
|
+
block = { marked: true, markerLine: i, lastLine: i, descLines: [descMarker[1]], default: '', annotations: {}, candidate: null }
|
|
252
309
|
continue
|
|
253
310
|
}
|
|
254
311
|
|
|
255
312
|
const comment = line.match(COMMENT_RE)
|
|
256
313
|
if (comment && line.trim().startsWith('#')) {
|
|
257
314
|
if (atDoc) { atDoc.descLines.push(comment[1]); atDoc.lastLine = i }
|
|
258
|
-
// Description lines precede the documented key
|
|
259
|
-
// exists, trailing comment lines belong to its example structure and
|
|
260
|
-
// only extend the span.
|
|
315
|
+
// Description lines precede the documented key.
|
|
261
316
|
else if (block && !block.candidate) { block.descLines.push(comment[1]); block.lastLine = i }
|
|
262
|
-
else if (block)
|
|
317
|
+
else if (block) {
|
|
318
|
+
// After the key: deeper lines, list items, and bare `#` lines belong
|
|
319
|
+
// to its commented-out example. Prose at the key's own depth or
|
|
320
|
+
// shallower is the description of the next commented-out sibling.
|
|
321
|
+
const [, before, after] = line.match(/^(\s*)#(\s*)/)
|
|
322
|
+
const proseIndent = before.length + Math.max(0, after.length - 1)
|
|
323
|
+
const text = comment[1].trim()
|
|
324
|
+
if (text === '' || text.startsWith('- ') || text === '-' || proseIndent > block.candidate.effIndent) {
|
|
325
|
+
block.lastLine = i
|
|
326
|
+
} else {
|
|
327
|
+
flushBlock()
|
|
328
|
+
suppressInnerIndent = -1
|
|
329
|
+
block = openUnmarkedBlock(i, comment[1])
|
|
330
|
+
}
|
|
331
|
+
}
|
|
263
332
|
continue
|
|
264
333
|
}
|
|
265
334
|
|
|
@@ -270,8 +339,9 @@ function parseValuesFile (yamlText, { attachRealKeys = false } = {}) {
|
|
|
270
339
|
flushAtDoc()
|
|
271
340
|
|
|
272
341
|
const realKey = line.match(REAL_KEY_RE)
|
|
273
|
-
if (attachRealKeys && realKey && block && !block.candidate && block.lastLine === i - 1) {
|
|
274
|
-
// helm-docs' own attachment: the block sits DIRECTLY above a
|
|
342
|
+
if (attachRealKeys && realKey && block && block.marked && !block.candidate && block.lastLine === i - 1) {
|
|
343
|
+
// helm-docs' own attachment: the marked block sits DIRECTLY above a
|
|
344
|
+
// real key.
|
|
275
345
|
const indent = realKey[1].length
|
|
276
346
|
while (stack.length > 0 && stack[stack.length - 1].indent >= indent) stack.pop()
|
|
277
347
|
const name = realKey[2].replace(/^"|"$/g, '')
|
|
@@ -43,51 +43,57 @@ async function fetchRemoteAntoraVersion(deps = {}) {
|
|
|
43
43
|
}
|
|
44
44
|
|
|
45
45
|
/**
|
|
46
|
-
*
|
|
46
|
+
* Reports whether a branch exists in the docs repository.
|
|
47
47
|
*
|
|
48
|
-
*
|
|
48
|
+
* @param {string} branch - Branch name, such as "main" or "v/25.1".
|
|
49
|
+
* @returns {boolean} True when `git ls-remote` finds the branch.
|
|
50
|
+
*/
|
|
51
|
+
function docsBranchExists(branch) {
|
|
52
|
+
const repo = 'https://github.com/redpanda-data/docs.git'
|
|
53
|
+
return spawnSync('git', ['ls-remote', '--exit-code', '--heads', repo, `refs/heads/${branch}`], {
|
|
54
|
+
stdio: 'ignore'
|
|
55
|
+
}).status === 0
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Determines the appropriate documentation branch for a given operator tag or branch based on the remote Antora version.
|
|
60
|
+
*
|
|
61
|
+
* Normalizes the input, extracts the major.minor version, and applies version-specific logic to select the correct branch. Verifies that the chosen branch exists in the remote repository.
|
|
62
|
+
* A ref with no version, such as `main`, `dev`, or a feature branch, holds content for the next release, which the docs repository keeps on `main`.
|
|
49
63
|
*
|
|
50
|
-
* @param {string}
|
|
64
|
+
* @param {string} operatorRef - The operator tag or branch to evaluate (for example, "operator/v25.1.2", "v25.1.2", "release/v25.1.x", or "main").
|
|
65
|
+
* @param {object} [deps] - Injectable for tests: { antoraVersion, branchExists, fetchImpl, token }.
|
|
51
66
|
* @returns {Promise<string>} The name of the documentation branch to use.
|
|
52
67
|
*
|
|
53
|
-
* @throws {Error} If the
|
|
68
|
+
* @throws {Error} If the Antora version cannot be fetched or if the determined branch does not exist in the remote repository.
|
|
54
69
|
*/
|
|
55
|
-
async function determineDocsBranch(
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
const ANTORA = await fetchRemoteAntoraVersion()
|
|
70
|
+
async function determineDocsBranch(operatorRef, deps = {}) {
|
|
71
|
+
const branchExists = deps.branchExists || docsBranchExists
|
|
72
|
+
// Strip any "operator/" or "release/" prefix
|
|
73
|
+
const TAG = operatorRef.replace(/^(?:operator|release)\//, '')
|
|
60
74
|
// Extract v<major>.<minor>
|
|
61
75
|
const mm = TAG.match(/^v(\d+\.\d+)/)
|
|
62
76
|
const filtered = mm ? `v${mm[1]}` : null
|
|
63
77
|
|
|
64
|
-
if (!filtered) {
|
|
65
|
-
throw new Error(`Could not parse major.minor from ${TAG}`)
|
|
66
|
-
}
|
|
67
|
-
// We started versioning the operator in line with Redpanda core versions. But when v2.4.x was the latest version, the docs were still on 25.1 and v25.1.x of the operator was still in beta. So we need to handle this special case.
|
|
68
78
|
let branch
|
|
69
|
-
if (filtered
|
|
70
|
-
|
|
79
|
+
if (!filtered) {
|
|
80
|
+
branch = 'main'
|
|
81
|
+
} else {
|
|
82
|
+
// Pull in the remote Antora version
|
|
83
|
+
const ANTORA = deps.antoraVersion || await fetchRemoteAntoraVersion(deps)
|
|
84
|
+
// We started versioning the operator in line with Redpanda core versions. But when v2.4.x was the latest version, the docs were still on 25.1 and v25.1.x of the operator was still in beta. So we need to handle this special case.
|
|
85
|
+
if (filtered === 'v2.4') {
|
|
86
|
+
branch = ANTORA === '25.1' ? 'main' : 'v/24.3'
|
|
87
|
+
// For all other versions use the v<major>.<minor> branch unless it is the current version, in which case we use 'main'.
|
|
88
|
+
} else if (filtered === `v${ANTORA}`) {
|
|
71
89
|
branch = 'main'
|
|
72
90
|
} else {
|
|
73
|
-
branch =
|
|
91
|
+
branch = `v/${filtered.slice(1)}`
|
|
74
92
|
}
|
|
75
|
-
// For all other versions use the v<major>.<minor> branch unless it is the current version, in which case we use 'main'.
|
|
76
|
-
} else if (filtered === `v${ANTORA}`) {
|
|
77
|
-
branch = 'main'
|
|
78
|
-
} else {
|
|
79
|
-
branch = `v/${filtered.slice(1)}`
|
|
80
93
|
}
|
|
81
94
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
const ref = `refs/heads/${branch}`
|
|
85
|
-
const ok = spawnSync('git', ['ls-remote', '--exit-code', '--heads', repo, ref], {
|
|
86
|
-
stdio: 'ignore'
|
|
87
|
-
}).status === 0
|
|
88
|
-
|
|
89
|
-
if (!ok) {
|
|
90
|
-
throw new Error(`Docs branch ${branch} not found in ${repo}`)
|
|
95
|
+
if (!branchExists(branch)) {
|
|
96
|
+
throw new Error(`Docs branch ${branch} not found in https://github.com/redpanda-data/docs.git`)
|
|
91
97
|
}
|
|
92
98
|
|
|
93
99
|
return branch
|