@redpanda-data/docs-extensions-and-macros 5.14.0 → 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 +252 -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/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' }
|
|
@@ -1203,6 +1205,12 @@ automation
|
|
|
1203
1205
|
// properties new in this release instead of one release late.
|
|
1204
1206
|
const extractedJsonPath = path.resolve(outputDir, 'attachments', `redpanda-properties-${newTag}.json`)
|
|
1205
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)
|
|
1206
1214
|
}
|
|
1207
1215
|
}
|
|
1208
1216
|
} catch (err) {
|
|
@@ -1599,108 +1607,6 @@ automation
|
|
|
1599
1607
|
}
|
|
1600
1608
|
})
|
|
1601
1609
|
|
|
1602
|
-
/**
|
|
1603
|
-
* validate rpk-overrides
|
|
1604
|
-
*
|
|
1605
|
-
* @description
|
|
1606
|
-
* Validates the rpk-overrides.json file against the JSON schema and checks for common issues:
|
|
1607
|
-
* - Schema compliance (required fields, valid types)
|
|
1608
|
-
* - Valid $ref references (no broken or circular refs)
|
|
1609
|
-
* - Valid command paths (compared against actual rpk command tree)
|
|
1610
|
-
* - Valid admonition locations (after_flags, after_usage, etc.)
|
|
1611
|
-
* - Valid platform values (linux, darwin, windows)
|
|
1612
|
-
*
|
|
1613
|
-
* @why
|
|
1614
|
-
* Catching override errors early prevents broken documentation. This command lets writers
|
|
1615
|
-
* validate their changes before generation, avoiding cryptic errors during the build process.
|
|
1616
|
-
*
|
|
1617
|
-
* @example
|
|
1618
|
-
* # Validate overrides with default paths
|
|
1619
|
-
* npx doc-tools validate rpk-overrides
|
|
1620
|
-
*
|
|
1621
|
-
* # Validate against a specific rpk tree (for complete command path validation)
|
|
1622
|
-
* npx doc-tools validate rpk-overrides --tree docs-data/rpk-v26.2.0.json
|
|
1623
|
-
*
|
|
1624
|
-
* # Validate a custom overrides file
|
|
1625
|
-
* npx doc-tools validate rpk-overrides --overrides my-overrides.json
|
|
1626
|
-
*
|
|
1627
|
-
* # Strict mode - exit with error code on validation failures
|
|
1628
|
-
* npx doc-tools validate rpk-overrides --strict
|
|
1629
|
-
*
|
|
1630
|
-
* @requirements
|
|
1631
|
-
* - rpk-overrides.schema.json in docs-data/
|
|
1632
|
-
*/
|
|
1633
|
-
automation
|
|
1634
|
-
.command('rpk-overrides')
|
|
1635
|
-
.description('Validate rpk-overrides.json against schema and check for common issues')
|
|
1636
|
-
.option('--overrides <path>', 'Path to overrides JSON file', 'docs-data/rpk-overrides.json')
|
|
1637
|
-
.option('--tree <path>', 'Path to rpk tree JSON file for command path validation (e.g., docs-data/rpk-v26.2.0.json)')
|
|
1638
|
-
.option('--strict', 'Exit with error code on validation failures')
|
|
1639
|
-
.action((options) => {
|
|
1640
|
-
try {
|
|
1641
|
-
const { loadAndValidateOverrides } = require('../tools/rpk-docs/validate-overrides.js')
|
|
1642
|
-
const repoRoot = findRepoRoot()
|
|
1643
|
-
const overridesPath = path.resolve(repoRoot, options.overrides)
|
|
1644
|
-
|
|
1645
|
-
// Load tree if provided for command path validation
|
|
1646
|
-
let commandTree = null
|
|
1647
|
-
if (options.tree) {
|
|
1648
|
-
const treePath = path.resolve(repoRoot, options.tree)
|
|
1649
|
-
if (!fs.existsSync(treePath)) {
|
|
1650
|
-
console.error(`Error: Tree file not found: ${treePath}`)
|
|
1651
|
-
process.exit(1)
|
|
1652
|
-
}
|
|
1653
|
-
const treeData = JSON.parse(fs.readFileSync(treePath, 'utf8'))
|
|
1654
|
-
commandTree = treeData.tree || treeData
|
|
1655
|
-
}
|
|
1656
|
-
|
|
1657
|
-
console.log(`Validating: ${overridesPath}`)
|
|
1658
|
-
if (commandTree) {
|
|
1659
|
-
console.log(`Comparing against tree from: ${options.tree}`)
|
|
1660
|
-
} else {
|
|
1661
|
-
console.log('Note: Skipping command path validation (no --tree provided)')
|
|
1662
|
-
}
|
|
1663
|
-
console.log('')
|
|
1664
|
-
|
|
1665
|
-
const { overrides, validation } = loadAndValidateOverrides(overridesPath, commandTree)
|
|
1666
|
-
|
|
1667
|
-
if (!overrides) {
|
|
1668
|
-
console.error('Error: Could not load overrides file')
|
|
1669
|
-
process.exit(1)
|
|
1670
|
-
}
|
|
1671
|
-
|
|
1672
|
-
// Print results
|
|
1673
|
-
console.log('=' .repeat(60))
|
|
1674
|
-
console.log('VALIDATION RESULTS')
|
|
1675
|
-
console.log('='.repeat(60))
|
|
1676
|
-
|
|
1677
|
-
if (validation.errors.length === 0 && validation.warnings.length === 0) {
|
|
1678
|
-
console.log('✓ No issues found')
|
|
1679
|
-
} else {
|
|
1680
|
-
console.log(validation.format())
|
|
1681
|
-
}
|
|
1682
|
-
|
|
1683
|
-
console.log('='.repeat(60))
|
|
1684
|
-
console.log(`Errors: ${validation.errors.length}`)
|
|
1685
|
-
console.log(`Warnings: ${validation.warnings.length}`)
|
|
1686
|
-
console.log('='.repeat(60))
|
|
1687
|
-
|
|
1688
|
-
// Exit with appropriate code
|
|
1689
|
-
if (options.strict && !validation.valid) {
|
|
1690
|
-
console.log('\n✗ Validation failed (strict mode)')
|
|
1691
|
-
process.exit(1)
|
|
1692
|
-
} else if (validation.valid) {
|
|
1693
|
-
console.log('\n✓ Validation passed')
|
|
1694
|
-
process.exit(0)
|
|
1695
|
-
} else {
|
|
1696
|
-
console.log('\n⚠ Validation completed with errors (use --strict to fail)')
|
|
1697
|
-
process.exit(0)
|
|
1698
|
-
}
|
|
1699
|
-
} catch (err) {
|
|
1700
|
-
console.error(`Error: ${err.message}`)
|
|
1701
|
-
process.exit(1)
|
|
1702
|
-
}
|
|
1703
|
-
})
|
|
1704
1610
|
|
|
1705
1611
|
|
|
1706
1612
|
/**
|
|
@@ -2373,6 +2279,110 @@ automation
|
|
|
2373
2279
|
|
|
2374
2280
|
const validation = new Command('validate').description('Validate docs data against internal sources of truth')
|
|
2375
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
|
+
|
|
2376
2386
|
/**
|
|
2377
2387
|
* @description Checks the enterprise features registry (the shared component's
|
|
2378
2388
|
* enterprise-features.yml in the docs repo) against the internal sources of
|
|
@@ -2465,6 +2475,146 @@ validation
|
|
|
2465
2475
|
}
|
|
2466
2476
|
})
|
|
2467
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
|
+
|
|
2468
2617
|
programCli.addCommand(automation)
|
|
2469
2618
|
programCli.addCommand(validation)
|
|
2619
|
+
programCli.addCommand(overridesGroup)
|
|
2470
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,
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP Tools - Lint embedded doc strings in engineering source
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
const path = require('path');
|
|
6
|
+
const { spawnSync } = require('child_process');
|
|
7
|
+
const { MAX_EXEC_BUFFER_SIZE, DEFAULT_COMMAND_TIMEOUT } = require('./utils');
|
|
8
|
+
|
|
9
|
+
// The CLI ships in this same package, so invoke it directly: this tool is
|
|
10
|
+
// designed to run inside ENGINEERING repos (redpanda, redpanda-operator,
|
|
11
|
+
// connect), where the docs-repo detection behind getDocToolsCommand does
|
|
12
|
+
// not apply.
|
|
13
|
+
const DOC_TOOLS_BIN = path.resolve(__dirname, '..', 'doc-tools.js');
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Lint user-facing doc strings embedded in engineering source code
|
|
17
|
+
* @param {Object} args - Arguments
|
|
18
|
+
* @param {string} args.repo - Path to the engineering checkout (required)
|
|
19
|
+
* @param {string} [args.surface] - Comma-separated surfaces (properties, metrics, rpk, helm, crd, connect)
|
|
20
|
+
* @param {string} [args.diff] - Base ref for declaration-anchored diff mode
|
|
21
|
+
* @param {string} [args.skip_rules] - Comma-separated rule ids to skip
|
|
22
|
+
* @param {string} [args.only_rules] - Comma-separated rule ids to run exclusively
|
|
23
|
+
* @returns {Object} Lint results (findings + summary, JSON)
|
|
24
|
+
*/
|
|
25
|
+
function lintDocStrings(args = {}) {
|
|
26
|
+
if (!args.repo) {
|
|
27
|
+
return {
|
|
28
|
+
success: false,
|
|
29
|
+
error: 'repo is required',
|
|
30
|
+
suggestion: 'Pass the absolute path of the engineering checkout to lint (for example, a local redpanda clone)'
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const cliArgs = [DOC_TOOLS_BIN, 'lint-strings', '--repo', args.repo, '--format', 'json'];
|
|
35
|
+
if (args.surface) cliArgs.push('--surface', args.surface);
|
|
36
|
+
if (args.diff) cliArgs.push('--diff', args.diff);
|
|
37
|
+
if (args.skip_rules) cliArgs.push('--skip-rules', args.skip_rules);
|
|
38
|
+
if (args.only_rules) cliArgs.push('--only-rules', args.only_rules);
|
|
39
|
+
|
|
40
|
+
try {
|
|
41
|
+
const result = spawnSync(process.execPath, cliArgs, {
|
|
42
|
+
encoding: 'utf8',
|
|
43
|
+
stdio: 'pipe',
|
|
44
|
+
maxBuffer: MAX_EXEC_BUFFER_SIZE,
|
|
45
|
+
timeout: DEFAULT_COMMAND_TIMEOUT
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
if (result.error) {
|
|
49
|
+
throw new Error(`Failed to execute command: ${result.error.message}`);
|
|
50
|
+
}
|
|
51
|
+
if (result.status !== 0) {
|
|
52
|
+
throw new Error(result.stderr || `Command failed with exit code ${result.status}`);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const parsed = JSON.parse(result.stdout);
|
|
56
|
+
return {
|
|
57
|
+
success: true,
|
|
58
|
+
summary: parsed.summary,
|
|
59
|
+
findings: parsed.findings,
|
|
60
|
+
unsupported_surfaces: parsed.unsupported_surfaces || []
|
|
61
|
+
};
|
|
62
|
+
} catch (err) {
|
|
63
|
+
return {
|
|
64
|
+
success: false,
|
|
65
|
+
error: err.message,
|
|
66
|
+
stdout: err.stdout || '',
|
|
67
|
+
stderr: err.stderr || '',
|
|
68
|
+
suggestion: 'Check that the repo path exists. The properties surface additionally needs python3 and network access on first run (venv + tree-sitter grammar); lint a specific --surface to skip it.'
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
module.exports = {
|
|
74
|
+
lintDocStrings
|
|
75
|
+
};
|