@redpanda-data/docs-extensions-and-macros 5.13.5 → 5.17.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 +88 -0
- package/bin/doc-tools.js +349 -102
- package/bin/mcp-tools/index.js +20 -0
- package/bin/mcp-tools/lint-doc-strings.js +75 -0
- package/bin/mcp-tools/overrides-audit.js +100 -0
- package/bin/mcp-tools/preview-doc-string.js +71 -0
- package/cli-utils/diff-utils.js +77 -0
- package/cli-utils/helm-commented-values.js +172 -23
- package/cli-utils/sync-schemas.js +144 -0
- package/docs-data/rpk-overrides.schema.json +4 -0
- package/package.json +2 -1
- package/tools/generate-cli-docs.js +28 -67
- package/tools/lint-strings/diff.js +137 -0
- package/tools/lint-strings/engine.js +163 -0
- package/tools/lint-strings/fixtures/connect/lint_connect.go +67 -0
- package/tools/lint-strings/fixtures/crd/crd-ref-docs-config.yaml +15 -0
- package/tools/lint-strings/fixtures/crd/lint_types.go +57 -0
- package/tools/lint-strings/fixtures/helm/lint_values.yaml +47 -0
- package/tools/lint-strings/fixtures/metrics/lint_probe.cc +41 -0
- package/tools/lint-strings/fixtures/properties/lint_fixture.cc +51 -0
- package/tools/lint-strings/fixtures/properties/lint_fixture.h +33 -0
- package/tools/lint-strings/fixtures/rpk/lint_cmd.go +69 -0
- package/tools/lint-strings/go-source.js +327 -0
- package/tools/lint-strings/index.js +229 -0
- package/tools/lint-strings/rules/common.js +148 -0
- package/tools/lint-strings/rules/verbatim-asciidoc.js +108 -0
- package/tools/lint-strings/source-text.js +41 -0
- package/tools/lint-strings/surfaces/connect.js +233 -0
- package/tools/lint-strings/surfaces/crd.js +239 -0
- package/tools/lint-strings/surfaces/helm.js +159 -0
- package/tools/lint-strings/surfaces/metrics.js +368 -0
- package/tools/lint-strings/surfaces/properties.js +216 -0
- package/tools/lint-strings/surfaces/rpk.js +377 -0
- package/tools/overrides-audit/README.adoc +152 -0
- package/tools/overrides-audit/adapters/connect.js +144 -0
- package/tools/overrides-audit/adapters/properties.js +117 -0
- package/tools/overrides-audit/adapters/rpk.js +192 -0
- package/tools/overrides-audit/classify.js +546 -0
- package/tools/overrides-audit/index.js +185 -0
- package/tools/preview-string/index.js +245 -0
- package/tools/property-extractor/Makefile +9 -4
- package/tools/property-extractor/parser.py +26 -0
- package/tools/property-extractor/property_extractor.py +41 -0
- package/tools/property-extractor/transformers.py +15 -2
- package/tools/rpk-docs/detect-platform-commands.js +8 -1
- package/tools/rpk-docs/scan-deprecated-commands.js +15 -18
package/bin/doc-tools-mcp.js
CHANGED
|
@@ -434,6 +434,94 @@ const tools = [
|
|
|
434
434
|
required: ['doc_type']
|
|
435
435
|
}
|
|
436
436
|
},
|
|
437
|
+
{
|
|
438
|
+
name: 'lint_doc_strings',
|
|
439
|
+
description: 'Lint user-facing doc strings embedded in engineering source code (property descriptions, rpk Short/Long/flag usage, metric help, Helm values comments, CRD field comments, connect field descriptions). These strings ship verbatim to docs.redpanda.com. Deterministic, suggest-only, with exact file:line spans. Run it inside an engineering checkout (redpanda, redpanda-operator, connect).',
|
|
440
|
+
inputSchema: {
|
|
441
|
+
type: 'object',
|
|
442
|
+
properties: {
|
|
443
|
+
repo: {
|
|
444
|
+
type: 'string',
|
|
445
|
+
description: 'Absolute path to the engineering checkout to lint (required). Nothing is cloned.'
|
|
446
|
+
},
|
|
447
|
+
surface: {
|
|
448
|
+
type: 'string',
|
|
449
|
+
description: 'Comma-separated surfaces to lint: properties, metrics, rpk, helm, crd, connect (optional, defaults to all)'
|
|
450
|
+
},
|
|
451
|
+
diff: {
|
|
452
|
+
type: 'string',
|
|
453
|
+
description: 'Base git ref for declaration-anchored diff mode: lint only declarations whose span intersects lines changed in <ref>...HEAD (optional)'
|
|
454
|
+
},
|
|
455
|
+
skip_rules: {
|
|
456
|
+
type: 'string',
|
|
457
|
+
description: 'Comma-separated rule ids to skip (optional)'
|
|
458
|
+
},
|
|
459
|
+
only_rules: {
|
|
460
|
+
type: 'string',
|
|
461
|
+
description: 'Comma-separated rule ids to run exclusively (optional)'
|
|
462
|
+
}
|
|
463
|
+
},
|
|
464
|
+
required: ['repo']
|
|
465
|
+
}
|
|
466
|
+
},
|
|
467
|
+
{
|
|
468
|
+
name: 'preview_doc_string',
|
|
469
|
+
description: 'Render ONE embedded doc string from local engineering source to the final snippet published on docs.redpanda.com: properties through the real extractor and Handlebars template (with an optional override pane showing docs-repo masking), rpk through the real formatDescription() transformer, and metrics/helm/crd/connect in their published output shapes.',
|
|
470
|
+
inputSchema: {
|
|
471
|
+
type: 'object',
|
|
472
|
+
properties: {
|
|
473
|
+
repo: {
|
|
474
|
+
type: 'string',
|
|
475
|
+
description: 'Absolute path to the engineering checkout (required)'
|
|
476
|
+
},
|
|
477
|
+
surface: {
|
|
478
|
+
type: 'string',
|
|
479
|
+
description: 'Which surface the declaration belongs to',
|
|
480
|
+
enum: ['properties', 'rpk', 'metrics', 'helm', 'crd', 'connect']
|
|
481
|
+
},
|
|
482
|
+
name: {
|
|
483
|
+
type: 'string',
|
|
484
|
+
description: 'Declaration name: property name, rpk command token or --flag, metric name, Helm key path, CRD json field (or Struct.field), connect component/field name'
|
|
485
|
+
},
|
|
486
|
+
overrides: {
|
|
487
|
+
type: 'string',
|
|
488
|
+
description: 'Path to a docs-repo overrides JSON (properties only): adds an "as shipped" pane and a MASKED-BY-OVERRIDE notice when the override differs (optional)'
|
|
489
|
+
}
|
|
490
|
+
},
|
|
491
|
+
required: ['repo', 'surface', 'name']
|
|
492
|
+
}
|
|
493
|
+
},
|
|
494
|
+
{
|
|
495
|
+
name: 'audit_overrides',
|
|
496
|
+
description: 'Audit docs-side override entries against extracted source strings. Classifies each override field as REDUNDANT (retire), UPSTREAMABLE (send prose upstream), KEEP_UNTIL_UPSTREAMED (markup-laden, stripped upstream candidate emitted), UPSTREAMABLE_SLOT, REDUNDANT_OR_UPSTREAMABLE, KEEP, or REVIEW. Writers use this to retire stale overrides and draft upstream source fixes.',
|
|
497
|
+
inputSchema: {
|
|
498
|
+
type: 'object',
|
|
499
|
+
properties: {
|
|
500
|
+
overrides: {
|
|
501
|
+
type: 'string',
|
|
502
|
+
description: 'Path to the overrides JSON file (for example "docs-data/property-overrides.json")'
|
|
503
|
+
},
|
|
504
|
+
extracted: {
|
|
505
|
+
type: 'string',
|
|
506
|
+
description: 'Path to the extracted source JSON: the property extractor\'s RAW --output file, without overrides applied (required for the properties surface, alternative to "repo")'
|
|
507
|
+
},
|
|
508
|
+
repo: {
|
|
509
|
+
type: 'string',
|
|
510
|
+
description: 'Redpanda checkout to extract raw source strings from (properties surface only, alternative to "extracted")'
|
|
511
|
+
},
|
|
512
|
+
surface: {
|
|
513
|
+
type: 'string',
|
|
514
|
+
description: 'Override surface to audit (optional, defaults to "properties")',
|
|
515
|
+
enum: ['properties', 'rpk', 'connect']
|
|
516
|
+
},
|
|
517
|
+
output: {
|
|
518
|
+
type: 'string',
|
|
519
|
+
description: 'Also write the JSON result to this file, relative to repo root (optional)'
|
|
520
|
+
}
|
|
521
|
+
},
|
|
522
|
+
required: ['overrides']
|
|
523
|
+
}
|
|
524
|
+
},
|
|
437
525
|
{
|
|
438
526
|
name: 'run_doc_tools_command',
|
|
439
527
|
description: 'Advanced: Run a raw doc-tools command. Only use this if none of the specific tools above fit your needs. Requires knowledge of doc-tools CLI syntax.',
|
package/bin/doc-tools.js
CHANGED
|
@@ -27,6 +27,7 @@ const {
|
|
|
27
27
|
generatePropertyComparisonReport,
|
|
28
28
|
updatePropertyOverridesWithVersion,
|
|
29
29
|
updatePropertiesJsonWithVersion,
|
|
30
|
+
repairPropertyAnchorsInJson,
|
|
30
31
|
cleanupOldDiffs,
|
|
31
32
|
resolveDiffBaseline
|
|
32
33
|
} = require('../cli-utils/diff-utils')
|
|
@@ -501,6 +502,7 @@ programCli
|
|
|
501
502
|
{ name: 'generate_cloud_regions', description: 'Generate cloud regions documentation' },
|
|
502
503
|
{ name: 'generate_bundle_openapi', description: 'Bundle OpenAPI specifications' },
|
|
503
504
|
{ name: 'review_generated_docs', description: 'Review generated documentation' },
|
|
505
|
+
{ name: 'audit_overrides', description: 'Audit docs-side overrides against extracted source strings' },
|
|
504
506
|
{ name: 'run_doc_tools_command', description: 'Run raw doc-tools command' },
|
|
505
507
|
{ name: 'get_job_status', description: 'Get background job status' },
|
|
506
508
|
{ name: 'list_jobs', description: 'List background jobs' }
|
|
@@ -529,6 +531,103 @@ programCli
|
|
|
529
531
|
}
|
|
530
532
|
})
|
|
531
533
|
|
|
534
|
+
/**
|
|
535
|
+
* @description Copy this package's docs-data/*.schema.json files into a
|
|
536
|
+
* content repo's own docs-data/ (the file that documents docs-data/x.json
|
|
537
|
+
* lives alongside x.json itself, so a writer can change both in one PR
|
|
538
|
+
* without waiting on a package release first). Run this after bumping
|
|
539
|
+
* @redpanda-data/docs-extensions-and-macros so the local copy picks up
|
|
540
|
+
* whatever changed upstream.
|
|
541
|
+
* @why Nothing keeps a content repo's schema copy in sync with this
|
|
542
|
+
* package's copy today, in either direction. A content repo's copy can
|
|
543
|
+
* legitimately be ahead of this package's (confirmed happening in
|
|
544
|
+
* practice: the docs repo's rpk-overrides.schema.json documents a real,
|
|
545
|
+
* working `asPartial` override field that this package's own copy is
|
|
546
|
+
* missing) — overwriting it in that case would delete real, correct
|
|
547
|
+
* documentation, not fix drift. This only overwrites when this package's
|
|
548
|
+
* copy is a strict superset of the destination; otherwise it reports which
|
|
549
|
+
* keys only the destination has and leaves the file alone (status
|
|
550
|
+
* 'diverged') unless --force is passed. --check never writes, for a CI
|
|
551
|
+
* gate.
|
|
552
|
+
* @example
|
|
553
|
+
* # Sync into ./docs-data (writes any missing or out-of-date schema)
|
|
554
|
+
* npx doc-tools sync-schemas
|
|
555
|
+
*
|
|
556
|
+
* # Report drift without writing -- exits 1 if anything is out of sync
|
|
557
|
+
* npx doc-tools sync-schemas --check
|
|
558
|
+
*
|
|
559
|
+
* # Sync into a non-default location
|
|
560
|
+
* npx doc-tools sync-schemas --dest path/to/docs-data
|
|
561
|
+
*
|
|
562
|
+
* # Overwrite even a destination that has diverged (rarely correct --
|
|
563
|
+
* # read the 'diverged' output first; it names what would be lost)
|
|
564
|
+
* npx doc-tools sync-schemas --force
|
|
565
|
+
* @requirements None.
|
|
566
|
+
*/
|
|
567
|
+
programCli
|
|
568
|
+
.command('sync-schemas')
|
|
569
|
+
.description("Sync this package's docs-data/*.schema.json into a content repo's docs-data/")
|
|
570
|
+
.option('--dest <path>', 'Destination docs-data directory', 'docs-data')
|
|
571
|
+
.option('--check', 'Report drift without writing; exit 1 if any schema is missing, out of date, or diverged')
|
|
572
|
+
.option('--force', 'Overwrite even a destination that has diverged (has content this package lacks)')
|
|
573
|
+
.action((options) => {
|
|
574
|
+
const { syncSchemas } = require('../cli-utils/sync-schemas')
|
|
575
|
+
|
|
576
|
+
try {
|
|
577
|
+
const { destDir, results, drift } = syncSchemas({
|
|
578
|
+
destDir: options.dest,
|
|
579
|
+
check: Boolean(options.check),
|
|
580
|
+
force: Boolean(options.force),
|
|
581
|
+
})
|
|
582
|
+
|
|
583
|
+
if (results.length === 0) {
|
|
584
|
+
console.log('No *.schema.json files found in this package to sync.')
|
|
585
|
+
process.exit(0)
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
console.log(`${options.check ? 'Checking' : 'Syncing'} against: ${destDir}`)
|
|
589
|
+
console.log('')
|
|
590
|
+
let hasUnresolvedDivergence = false
|
|
591
|
+
for (const { name, status, destOnlyPaths } of results) {
|
|
592
|
+
const label = {
|
|
593
|
+
unchanged: '✓ up to date',
|
|
594
|
+
created: '+ created',
|
|
595
|
+
updated: '↻ updated',
|
|
596
|
+
diverged: options.force ? '↻ updated (forced)' : '⚠ diverged, left alone',
|
|
597
|
+
}[status]
|
|
598
|
+
console.log(` ${label} ${name}`)
|
|
599
|
+
if (status === 'diverged' && !options.force) {
|
|
600
|
+
hasUnresolvedDivergence = true
|
|
601
|
+
for (const p of destOnlyPaths) console.log(` only in the destination: ${p}`)
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
console.log('')
|
|
605
|
+
|
|
606
|
+
if (options.check) {
|
|
607
|
+
if (drift) {
|
|
608
|
+
const hint = hasUnresolvedDivergence
|
|
609
|
+
? 'A diverged file needs a human decision (see paths above), not just a re-run.'
|
|
610
|
+
: 'Run `npx doc-tools sync-schemas` to fix.'
|
|
611
|
+
console.log(`✗ One or more schemas are out of date. ${hint}`)
|
|
612
|
+
process.exit(1)
|
|
613
|
+
}
|
|
614
|
+
console.log('✓ All schemas are in sync.')
|
|
615
|
+
process.exit(0)
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
if (hasUnresolvedDivergence) {
|
|
619
|
+
console.log('⚠ Synced what was safe to sync. One or more files diverged and were left alone -- see paths above. Re-run with --force only after confirming the destination-only content should be lost.')
|
|
620
|
+
process.exit(1)
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
console.log(drift ? '✓ Synced.' : '✓ Already in sync -- nothing to do.')
|
|
624
|
+
process.exit(0)
|
|
625
|
+
} catch (err) {
|
|
626
|
+
console.error(`Error: ${err.message}`)
|
|
627
|
+
process.exit(1)
|
|
628
|
+
}
|
|
629
|
+
})
|
|
630
|
+
|
|
532
631
|
/**
|
|
533
632
|
* @description Show MCP server version information including available tools
|
|
534
633
|
* and optionally usage statistics from previous sessions.
|
|
@@ -1106,6 +1205,12 @@ automation
|
|
|
1106
1205
|
// properties new in this release instead of one release late.
|
|
1107
1206
|
const extractedJsonPath = path.resolve(outputDir, 'attachments', `redpanda-properties-${newTag}.json`)
|
|
1108
1207
|
updatePropertiesJsonWithVersion(extractedJsonPath, diffData, newTag)
|
|
1208
|
+
// Repair anchors HERE, in Phase 2, so the attachment the docs UI
|
|
1209
|
+
// reads and the partials Phase 3 renders come from the same
|
|
1210
|
+
// corrected text. Normalizing only in Phase 3 left the two
|
|
1211
|
+
// disagreeing: the partials linked <<flush-bytes>> while the
|
|
1212
|
+
// published attachment still said <<flushbytes>>.
|
|
1213
|
+
repairPropertyAnchorsInJson(extractedJsonPath)
|
|
1109
1214
|
}
|
|
1110
1215
|
}
|
|
1111
1216
|
} catch (err) {
|
|
@@ -1502,108 +1607,6 @@ automation
|
|
|
1502
1607
|
}
|
|
1503
1608
|
})
|
|
1504
1609
|
|
|
1505
|
-
/**
|
|
1506
|
-
* validate rpk-overrides
|
|
1507
|
-
*
|
|
1508
|
-
* @description
|
|
1509
|
-
* Validates the rpk-overrides.json file against the JSON schema and checks for common issues:
|
|
1510
|
-
* - Schema compliance (required fields, valid types)
|
|
1511
|
-
* - Valid $ref references (no broken or circular refs)
|
|
1512
|
-
* - Valid command paths (compared against actual rpk command tree)
|
|
1513
|
-
* - Valid admonition locations (after_flags, after_usage, etc.)
|
|
1514
|
-
* - Valid platform values (linux, darwin, windows)
|
|
1515
|
-
*
|
|
1516
|
-
* @why
|
|
1517
|
-
* Catching override errors early prevents broken documentation. This command lets writers
|
|
1518
|
-
* validate their changes before generation, avoiding cryptic errors during the build process.
|
|
1519
|
-
*
|
|
1520
|
-
* @example
|
|
1521
|
-
* # Validate overrides with default paths
|
|
1522
|
-
* npx doc-tools validate rpk-overrides
|
|
1523
|
-
*
|
|
1524
|
-
* # Validate against a specific rpk tree (for complete command path validation)
|
|
1525
|
-
* npx doc-tools validate rpk-overrides --tree docs-data/rpk-v26.2.0.json
|
|
1526
|
-
*
|
|
1527
|
-
* # Validate a custom overrides file
|
|
1528
|
-
* npx doc-tools validate rpk-overrides --overrides my-overrides.json
|
|
1529
|
-
*
|
|
1530
|
-
* # Strict mode - exit with error code on validation failures
|
|
1531
|
-
* npx doc-tools validate rpk-overrides --strict
|
|
1532
|
-
*
|
|
1533
|
-
* @requirements
|
|
1534
|
-
* - rpk-overrides.schema.json in docs-data/
|
|
1535
|
-
*/
|
|
1536
|
-
automation
|
|
1537
|
-
.command('rpk-overrides')
|
|
1538
|
-
.description('Validate rpk-overrides.json against schema and check for common issues')
|
|
1539
|
-
.option('--overrides <path>', 'Path to overrides JSON file', 'docs-data/rpk-overrides.json')
|
|
1540
|
-
.option('--tree <path>', 'Path to rpk tree JSON file for command path validation (e.g., docs-data/rpk-v26.2.0.json)')
|
|
1541
|
-
.option('--strict', 'Exit with error code on validation failures')
|
|
1542
|
-
.action((options) => {
|
|
1543
|
-
try {
|
|
1544
|
-
const { loadAndValidateOverrides } = require('../tools/rpk-docs/validate-overrides.js')
|
|
1545
|
-
const repoRoot = findRepoRoot()
|
|
1546
|
-
const overridesPath = path.resolve(repoRoot, options.overrides)
|
|
1547
|
-
|
|
1548
|
-
// Load tree if provided for command path validation
|
|
1549
|
-
let commandTree = null
|
|
1550
|
-
if (options.tree) {
|
|
1551
|
-
const treePath = path.resolve(repoRoot, options.tree)
|
|
1552
|
-
if (!fs.existsSync(treePath)) {
|
|
1553
|
-
console.error(`Error: Tree file not found: ${treePath}`)
|
|
1554
|
-
process.exit(1)
|
|
1555
|
-
}
|
|
1556
|
-
const treeData = JSON.parse(fs.readFileSync(treePath, 'utf8'))
|
|
1557
|
-
commandTree = treeData.tree || treeData
|
|
1558
|
-
}
|
|
1559
|
-
|
|
1560
|
-
console.log(`Validating: ${overridesPath}`)
|
|
1561
|
-
if (commandTree) {
|
|
1562
|
-
console.log(`Comparing against tree from: ${options.tree}`)
|
|
1563
|
-
} else {
|
|
1564
|
-
console.log('Note: Skipping command path validation (no --tree provided)')
|
|
1565
|
-
}
|
|
1566
|
-
console.log('')
|
|
1567
|
-
|
|
1568
|
-
const { overrides, validation } = loadAndValidateOverrides(overridesPath, commandTree)
|
|
1569
|
-
|
|
1570
|
-
if (!overrides) {
|
|
1571
|
-
console.error('Error: Could not load overrides file')
|
|
1572
|
-
process.exit(1)
|
|
1573
|
-
}
|
|
1574
|
-
|
|
1575
|
-
// Print results
|
|
1576
|
-
console.log('=' .repeat(60))
|
|
1577
|
-
console.log('VALIDATION RESULTS')
|
|
1578
|
-
console.log('='.repeat(60))
|
|
1579
|
-
|
|
1580
|
-
if (validation.errors.length === 0 && validation.warnings.length === 0) {
|
|
1581
|
-
console.log('✓ No issues found')
|
|
1582
|
-
} else {
|
|
1583
|
-
console.log(validation.format())
|
|
1584
|
-
}
|
|
1585
|
-
|
|
1586
|
-
console.log('='.repeat(60))
|
|
1587
|
-
console.log(`Errors: ${validation.errors.length}`)
|
|
1588
|
-
console.log(`Warnings: ${validation.warnings.length}`)
|
|
1589
|
-
console.log('='.repeat(60))
|
|
1590
|
-
|
|
1591
|
-
// Exit with appropriate code
|
|
1592
|
-
if (options.strict && !validation.valid) {
|
|
1593
|
-
console.log('\n✗ Validation failed (strict mode)')
|
|
1594
|
-
process.exit(1)
|
|
1595
|
-
} else if (validation.valid) {
|
|
1596
|
-
console.log('\n✓ Validation passed')
|
|
1597
|
-
process.exit(0)
|
|
1598
|
-
} else {
|
|
1599
|
-
console.log('\n⚠ Validation completed with errors (use --strict to fail)')
|
|
1600
|
-
process.exit(0)
|
|
1601
|
-
}
|
|
1602
|
-
} catch (err) {
|
|
1603
|
-
console.error(`Error: ${err.message}`)
|
|
1604
|
-
process.exit(1)
|
|
1605
|
-
}
|
|
1606
|
-
})
|
|
1607
1610
|
|
|
1608
1611
|
|
|
1609
1612
|
/**
|
|
@@ -2276,6 +2279,110 @@ automation
|
|
|
2276
2279
|
|
|
2277
2280
|
const validation = new Command('validate').description('Validate docs data against internal sources of truth')
|
|
2278
2281
|
|
|
2282
|
+
/**
|
|
2283
|
+
* validate rpk-overrides
|
|
2284
|
+
*
|
|
2285
|
+
* @description
|
|
2286
|
+
* Validates the rpk-overrides.json file against the JSON schema and checks for common issues:
|
|
2287
|
+
* - Schema compliance (required fields, valid types)
|
|
2288
|
+
* - Valid $ref references (no broken or circular refs)
|
|
2289
|
+
* - Valid command paths (compared against actual rpk command tree)
|
|
2290
|
+
* - Valid admonition locations (after_flags, after_usage, etc.)
|
|
2291
|
+
* - Valid platform values (linux, darwin, windows)
|
|
2292
|
+
*
|
|
2293
|
+
* @why
|
|
2294
|
+
* Catching override errors early prevents broken documentation. This command lets writers
|
|
2295
|
+
* validate their changes before generation, avoiding cryptic errors during the build process.
|
|
2296
|
+
*
|
|
2297
|
+
* @example
|
|
2298
|
+
* # Validate overrides with default paths
|
|
2299
|
+
* npx doc-tools validate rpk-overrides
|
|
2300
|
+
*
|
|
2301
|
+
* # Validate against a specific rpk tree (for complete command path validation)
|
|
2302
|
+
* npx doc-tools validate rpk-overrides --tree docs-data/rpk-v26.2.0.json
|
|
2303
|
+
*
|
|
2304
|
+
* # Validate a custom overrides file
|
|
2305
|
+
* npx doc-tools validate rpk-overrides --overrides my-overrides.json
|
|
2306
|
+
*
|
|
2307
|
+
* # Strict mode - exit with error code on validation failures
|
|
2308
|
+
* npx doc-tools validate rpk-overrides --strict
|
|
2309
|
+
*
|
|
2310
|
+
* @requirements
|
|
2311
|
+
* - rpk-overrides.schema.json in docs-data/
|
|
2312
|
+
*/
|
|
2313
|
+
validation
|
|
2314
|
+
.command('rpk-overrides')
|
|
2315
|
+
.description('Validate rpk-overrides.json against schema and check for common issues')
|
|
2316
|
+
.option('--overrides <path>', 'Path to overrides JSON file', 'docs-data/rpk-overrides.json')
|
|
2317
|
+
.option('--tree <path>', 'Path to rpk tree JSON file for command path validation (e.g., docs-data/rpk-v26.2.0.json)')
|
|
2318
|
+
.option('--strict', 'Exit with error code on validation failures')
|
|
2319
|
+
.action((options) => {
|
|
2320
|
+
try {
|
|
2321
|
+
const { loadAndValidateOverrides } = require('../tools/rpk-docs/validate-overrides.js')
|
|
2322
|
+
const repoRoot = findRepoRoot()
|
|
2323
|
+
const overridesPath = path.resolve(repoRoot, options.overrides)
|
|
2324
|
+
|
|
2325
|
+
// Load tree if provided for command path validation
|
|
2326
|
+
let commandTree = null
|
|
2327
|
+
if (options.tree) {
|
|
2328
|
+
const treePath = path.resolve(repoRoot, options.tree)
|
|
2329
|
+
if (!fs.existsSync(treePath)) {
|
|
2330
|
+
console.error(`Error: Tree file not found: ${treePath}`)
|
|
2331
|
+
process.exit(1)
|
|
2332
|
+
}
|
|
2333
|
+
const treeData = JSON.parse(fs.readFileSync(treePath, 'utf8'))
|
|
2334
|
+
commandTree = treeData.tree || treeData
|
|
2335
|
+
}
|
|
2336
|
+
|
|
2337
|
+
console.log(`Validating: ${overridesPath}`)
|
|
2338
|
+
if (commandTree) {
|
|
2339
|
+
console.log(`Comparing against tree from: ${options.tree}`)
|
|
2340
|
+
} else {
|
|
2341
|
+
console.log('Note: Skipping command path validation (no --tree provided)')
|
|
2342
|
+
}
|
|
2343
|
+
console.log('')
|
|
2344
|
+
|
|
2345
|
+
const { overrides, validation } = loadAndValidateOverrides(overridesPath, commandTree)
|
|
2346
|
+
|
|
2347
|
+
if (!overrides) {
|
|
2348
|
+
console.error('Error: Could not load overrides file')
|
|
2349
|
+
process.exit(1)
|
|
2350
|
+
}
|
|
2351
|
+
|
|
2352
|
+
// Print results
|
|
2353
|
+
console.log('=' .repeat(60))
|
|
2354
|
+
console.log('VALIDATION RESULTS')
|
|
2355
|
+
console.log('='.repeat(60))
|
|
2356
|
+
|
|
2357
|
+
if (validation.errors.length === 0 && validation.warnings.length === 0) {
|
|
2358
|
+
console.log('✓ No issues found')
|
|
2359
|
+
} else {
|
|
2360
|
+
console.log(validation.format())
|
|
2361
|
+
}
|
|
2362
|
+
|
|
2363
|
+
console.log('='.repeat(60))
|
|
2364
|
+
console.log(`Errors: ${validation.errors.length}`)
|
|
2365
|
+
console.log(`Warnings: ${validation.warnings.length}`)
|
|
2366
|
+
console.log('='.repeat(60))
|
|
2367
|
+
|
|
2368
|
+
// Exit with appropriate code
|
|
2369
|
+
if (options.strict && !validation.valid) {
|
|
2370
|
+
console.log('\n✗ Validation failed (strict mode)')
|
|
2371
|
+
process.exit(1)
|
|
2372
|
+
} else if (validation.valid) {
|
|
2373
|
+
console.log('\n✓ Validation passed')
|
|
2374
|
+
process.exit(0)
|
|
2375
|
+
} else {
|
|
2376
|
+
console.log('\n⚠ Validation completed with errors (use --strict to fail)')
|
|
2377
|
+
process.exit(0)
|
|
2378
|
+
}
|
|
2379
|
+
} catch (err) {
|
|
2380
|
+
console.error(`Error: ${err.message}`)
|
|
2381
|
+
process.exit(1)
|
|
2382
|
+
}
|
|
2383
|
+
})
|
|
2384
|
+
|
|
2385
|
+
|
|
2279
2386
|
/**
|
|
2280
2387
|
* @description Checks the enterprise features registry (the shared component's
|
|
2281
2388
|
* enterprise-features.yml in the docs repo) against the internal sources of
|
|
@@ -2368,6 +2475,146 @@ validation
|
|
|
2368
2475
|
}
|
|
2369
2476
|
})
|
|
2370
2477
|
|
|
2478
|
+
/**
|
|
2479
|
+
* lint-strings
|
|
2480
|
+
*
|
|
2481
|
+
* @description
|
|
2482
|
+
* Deterministic lint for user-facing doc strings embedded in engineering
|
|
2483
|
+
* source code (property descriptions, metric help strings, ...). These
|
|
2484
|
+
* strings ship verbatim to docs.redpanda.com, so this command surfaces
|
|
2485
|
+
* quality problems (empty descriptions, broken markup, name-echo
|
|
2486
|
+
* tautologies, convention drift) at write time, with exact file:line spans
|
|
2487
|
+
* for each declaration.
|
|
2488
|
+
*
|
|
2489
|
+
* @why
|
|
2490
|
+
* The docs team currently patches bad source strings after the fact via
|
|
2491
|
+
* override files. Linting where engineers write the strings - including a
|
|
2492
|
+
* declaration-anchored --diff mode for PR reviews - retires that drift debt.
|
|
2493
|
+
*
|
|
2494
|
+
* @example
|
|
2495
|
+
* # Lint all supported surfaces in a local redpanda checkout
|
|
2496
|
+
* npx doc-tools lint-strings --repo ~/redpanda
|
|
2497
|
+
*
|
|
2498
|
+
* # Lint only properties, machine-readable
|
|
2499
|
+
* npx doc-tools lint-strings --repo ~/redpanda --surface properties --format json
|
|
2500
|
+
*
|
|
2501
|
+
* # PR mode: only declarations whose span intersects the diff
|
|
2502
|
+
* npx doc-tools lint-strings --repo ~/redpanda --diff origin/dev
|
|
2503
|
+
*/
|
|
2504
|
+
programCli
|
|
2505
|
+
.command('lint-strings')
|
|
2506
|
+
.description('Lint user-facing doc strings embedded in engineering source code (properties, metrics, ...)')
|
|
2507
|
+
.requiredOption('--repo <path>', 'Path to an existing engineering checkout (for example, a local redpanda clone). Nothing is cloned.')
|
|
2508
|
+
.option('--surface <list>', 'Comma-separated surfaces to lint (default: all registered). Registered: properties, metrics, rpk, helm, crd, connect')
|
|
2509
|
+
.option('--diff <base>', 'Declaration-anchored diff mode: lint only declarations whose full span intersects lines changed in <base>...HEAD')
|
|
2510
|
+
.option('--format <format>', 'Output format: human or json', 'human')
|
|
2511
|
+
.option('--skip-rules <list>', 'Comma-separated rule ids to skip')
|
|
2512
|
+
.option('--only-rules <list>', 'Comma-separated rule ids to run exclusively')
|
|
2513
|
+
.option('--strict', 'Exit 1 when any error-severity finding exists (default: always exit 0 - suggest, never block)')
|
|
2514
|
+
.action((options) => {
|
|
2515
|
+
const { runCli } = require('../tools/lint-strings')
|
|
2516
|
+
runCli(options)
|
|
2517
|
+
})
|
|
2518
|
+
|
|
2519
|
+
/**
|
|
2520
|
+
* preview-string
|
|
2521
|
+
*
|
|
2522
|
+
* @description
|
|
2523
|
+
* Render ONE doc-string declaration from local engineering source to the
|
|
2524
|
+
* final published snippet: properties through the real extractor +
|
|
2525
|
+
* Handlebars template (two panes when --overrides shows a docs-repo
|
|
2526
|
+
* override masking the source string), rpk through the real
|
|
2527
|
+
* formatDescription() transformer, and metrics/helm/crd/connect in their
|
|
2528
|
+
* published output shapes.
|
|
2529
|
+
*
|
|
2530
|
+
* @why
|
|
2531
|
+
* Engineers can see what their embedded string becomes on
|
|
2532
|
+
* docs.redpanda.com BEFORE it ships - including whether an override in the
|
|
2533
|
+
* docs repo would silently mask their fix.
|
|
2534
|
+
*
|
|
2535
|
+
* @example
|
|
2536
|
+
* # What does this property's docs section look like?
|
|
2537
|
+
* npx doc-tools preview-string --repo ~/redpanda --surface properties --name log_segment_size
|
|
2538
|
+
*
|
|
2539
|
+
* # Is my override masking the source string?
|
|
2540
|
+
* npx doc-tools preview-string --repo ~/redpanda --surface properties --name log_segment_size --overrides ~/docs/docs-data/property-overrides.json
|
|
2541
|
+
*
|
|
2542
|
+
* # What does formatDescription do to my rpk Long text?
|
|
2543
|
+
* npx doc-tools preview-string --repo ~/redpanda --surface rpk --name health
|
|
2544
|
+
*/
|
|
2545
|
+
programCli
|
|
2546
|
+
.command('preview-string')
|
|
2547
|
+
.description('Render one embedded doc string (property, rpk command/flag, metric, helm key, CRD field, connect field) as it will publish')
|
|
2548
|
+
.requiredOption('--repo <path>', 'Path to an existing engineering checkout. Nothing is cloned.')
|
|
2549
|
+
.requiredOption('--surface <surface>', 'One of: properties, rpk, metrics, helm, crd, connect')
|
|
2550
|
+
.requiredOption('--name <name>', 'Declaration name: property name, rpk command token or --flag, metric name, helm key path, CRD json field (or Struct.field), connect component/field name')
|
|
2551
|
+
.option('--overrides <path>', 'Docs-repo overrides JSON (properties only): adds an "as shipped" pane and a MASKED-BY-OVERRIDE notice when the override differs')
|
|
2552
|
+
.action((options) => {
|
|
2553
|
+
const { runCli } = require('../tools/preview-string')
|
|
2554
|
+
runCli(options)
|
|
2555
|
+
})
|
|
2556
|
+
|
|
2557
|
+
// ====================================================================
|
|
2558
|
+
// OVERRIDES COMMANDS
|
|
2559
|
+
// ====================================================================
|
|
2560
|
+
const overridesGroup = new Command('overrides').description('Audit docs-side override files against extracted source strings')
|
|
2561
|
+
|
|
2562
|
+
/**
|
|
2563
|
+
* @description Field-level classification of override entries against the
|
|
2564
|
+
* strings extracted from engineering source. Each override field classifies
|
|
2565
|
+
* as REDUNDANT (source already matches; retire it), UPSTREAMABLE (send the
|
|
2566
|
+
* prose upstream), KEEP_UNTIL_UPSTREAMED (markup-laden SPLIT case with a
|
|
2567
|
+
* stripped upstream candidate), UPSTREAMABLE_SLOT (migrate to a source
|
|
2568
|
+
* metadata slot), REDUNDANT_OR_UPSTREAMABLE (needs a human ruling), KEEP
|
|
2569
|
+
* (docs enrichment by design), or REVIEW (possible source bug; never
|
|
2570
|
+
* auto-delete). See tools/overrides-audit/README.adoc for the full rules
|
|
2571
|
+
* and the upstream_ref policy.
|
|
2572
|
+
*
|
|
2573
|
+
* @why Description overrides are stopgaps awaiting an upstream source fix;
|
|
2574
|
+
* once the fixed string ships in a release they silently mask all future
|
|
2575
|
+
* source improvements. This audit powers the retirement loop (delete
|
|
2576
|
+
* REDUNDANT fields on each release regeneration) and the upstreaming loop
|
|
2577
|
+
* (draft source PRs from the UPSTREAMABLE/SPLIT candidates).
|
|
2578
|
+
*
|
|
2579
|
+
* @example
|
|
2580
|
+
* # Audit the docs repo property overrides against a raw extraction
|
|
2581
|
+
* npx doc-tools overrides audit \
|
|
2582
|
+
* --overrides docs-data/property-overrides.json \
|
|
2583
|
+
* --extracted tools/property-extractor/gen/properties-output.json \
|
|
2584
|
+
* --format human
|
|
2585
|
+
*
|
|
2586
|
+
* @requirements
|
|
2587
|
+
* The --extracted file must be the property extractor's RAW output (its
|
|
2588
|
+
* --output file, without overrides applied). The enhanced output and the
|
|
2589
|
+
* versioned redpanda-properties-<tag>.json attachments already have
|
|
2590
|
+
* overrides applied, so auditing against them classifies everything
|
|
2591
|
+
* REDUNDANT.
|
|
2592
|
+
*/
|
|
2593
|
+
overridesGroup
|
|
2594
|
+
.command('audit')
|
|
2595
|
+
.description('Classify each override field as redundant, upstreamable, or keep against extracted source strings')
|
|
2596
|
+
.requiredOption('--overrides <path>', 'Path to the overrides JSON file (for example docs-data/property-overrides.json)')
|
|
2597
|
+
.option('--extracted <path>', 'Path to the extracted source JSON (property extractor raw output; required for the properties surface)')
|
|
2598
|
+
.addOption(new Option('--surface <surface>', 'Override surface to audit').choices(['properties', 'rpk', 'connect']).default('properties'))
|
|
2599
|
+
.addOption(new Option('--format <format>', 'Output format').choices(['json', 'human']).default('json'))
|
|
2600
|
+
.option('--repo <path>', 'Redpanda checkout to extract raw source strings from (alternative to --extracted)')
|
|
2601
|
+
.option('--output <path>', 'Also write the JSON result to this file')
|
|
2602
|
+
.action((options) => {
|
|
2603
|
+
const { runAudit, formatHumanReport } = require('../tools/overrides-audit')
|
|
2604
|
+
try {
|
|
2605
|
+
const result = runAudit(options)
|
|
2606
|
+
if (options.output) {
|
|
2607
|
+
fs.mkdirSync(path.dirname(path.resolve(options.output)), { recursive: true })
|
|
2608
|
+
fs.writeFileSync(path.resolve(options.output), JSON.stringify(result, null, 2) + '\n')
|
|
2609
|
+
console.error(`JSON result written to ${options.output}`)
|
|
2610
|
+
}
|
|
2611
|
+
console.log(options.format === 'human' ? formatHumanReport(result) : JSON.stringify(result, null, 2))
|
|
2612
|
+
} catch (err) {
|
|
2613
|
+
fail(err.message)
|
|
2614
|
+
}
|
|
2615
|
+
})
|
|
2616
|
+
|
|
2371
2617
|
programCli.addCommand(automation)
|
|
2372
2618
|
programCli.addCommand(validation)
|
|
2619
|
+
programCli.addCommand(overridesGroup)
|
|
2373
2620
|
programCli.parse(process.argv)
|
package/bin/mcp-tools/index.js
CHANGED
|
@@ -25,6 +25,11 @@ const { generateBundleOpenApi } = require('./openapi');
|
|
|
25
25
|
|
|
26
26
|
// Review tools
|
|
27
27
|
const { reviewGeneratedDocs, generateReviewReport } = require('./generated-docs-review');
|
|
28
|
+
const { auditOverrides } = require('./overrides-audit');
|
|
29
|
+
|
|
30
|
+
// Doc-string tools (run in engineering repos)
|
|
31
|
+
const { lintDocStrings } = require('./lint-doc-strings');
|
|
32
|
+
const { previewDocString } = require('./preview-doc-string');
|
|
28
33
|
|
|
29
34
|
// Job queue
|
|
30
35
|
const { initializeJobQueue, createJob, getJob, listJobs, cleanupOldJobs } = require('./job-queue');
|
|
@@ -89,6 +94,18 @@ function executeTool(toolName, args = {}) {
|
|
|
89
94
|
result = reviewGeneratedDocs(args);
|
|
90
95
|
break;
|
|
91
96
|
|
|
97
|
+
case 'lint_doc_strings':
|
|
98
|
+
result = lintDocStrings(args);
|
|
99
|
+
break;
|
|
100
|
+
|
|
101
|
+
case 'preview_doc_string':
|
|
102
|
+
result = previewDocString(args);
|
|
103
|
+
break;
|
|
104
|
+
|
|
105
|
+
case 'audit_overrides':
|
|
106
|
+
result = auditOverrides(args);
|
|
107
|
+
break;
|
|
108
|
+
|
|
92
109
|
case 'run_doc_tools_command': {
|
|
93
110
|
// Validate and execute raw doc-tools command
|
|
94
111
|
if (!args || typeof args !== 'object') {
|
|
@@ -250,6 +267,9 @@ module.exports = {
|
|
|
250
267
|
generateBundleOpenApi,
|
|
251
268
|
reviewGeneratedDocs,
|
|
252
269
|
generateReviewReport,
|
|
270
|
+
lintDocStrings,
|
|
271
|
+
previewDocString,
|
|
272
|
+
auditOverrides,
|
|
253
273
|
|
|
254
274
|
// Job queue
|
|
255
275
|
initializeJobQueue,
|