@redpanda-data/docs-extensions-and-macros 4.9.0 → 4.10.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.js +197 -3
- package/cli-utils/install-test-dependencies.sh +474 -106
- package/package.json +9 -3
- package/tools/bundle-openapi.js +814 -0
- package/tools/redpanda-connect/generate-rpcn-connector-docs.js +96 -2
- package/tools/redpanda-connect/helpers/bloblangExample.js +42 -0
- package/tools/redpanda-connect/helpers/index.js +4 -3
- package/tools/redpanda-connect/helpers/renderConnectFields.js +32 -5
- package/tools/redpanda-connect/report-delta.js +101 -0
- package/tools/redpanda-connect/templates/bloblang-function.hbs +28 -0
package/bin/doc-tools.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
const { execSync, spawnSync } = require('child_process');
|
|
4
4
|
const os = require('os');
|
|
5
|
-
const { Command } = require('commander');
|
|
5
|
+
const { Command, Option } = require('commander');
|
|
6
6
|
const path = require('path');
|
|
7
7
|
const yaml = require('yaml');
|
|
8
8
|
const fs = require('fs');
|
|
@@ -676,6 +676,7 @@ automation
|
|
|
676
676
|
.description('Generate RPCN connector docs and diff changes since the last version')
|
|
677
677
|
.option('-d, --data-dir <path>', 'Directory where versioned connect JSON files live', path.resolve(process.cwd(), 'docs-data'))
|
|
678
678
|
.option('--old-data <path>', 'Optional override for old data file (for diff)')
|
|
679
|
+
.option('--update-whats-new', 'Update whats-new.adoc with new section from diff JSON')
|
|
679
680
|
.option('-f, --fetch-connectors', 'Fetch latest connector data using rpk')
|
|
680
681
|
.option('-m, --draft-missing', 'Generate full-doc drafts for connectors missing in output')
|
|
681
682
|
.option('--csv <path>', 'Path to connector metadata CSV file', 'internal/plugins/info.csv')
|
|
@@ -683,7 +684,9 @@ automation
|
|
|
683
684
|
.option('--template-intro <path>', 'Intro section partial template', path.resolve(__dirname, '../tools/redpanda-connect/templates/intro.hbs'))
|
|
684
685
|
.option('--template-fields <path>', 'Fields section partial template', path.resolve(__dirname, '../tools/redpanda-connect/templates/fields-partials.hbs'))
|
|
685
686
|
.option('--template-examples <path>', 'Examples section partial template', path.resolve(__dirname, '../tools/redpanda-connect/templates/examples-partials.hbs'))
|
|
687
|
+
.option('--template-bloblang <path>', 'Custom Handlebars template for bloblang function/method partials')
|
|
686
688
|
.option('--overrides <path>', 'Optional JSON file with overrides')
|
|
689
|
+
.option('--include-bloblang', 'Include Bloblang functions and methods in generation')
|
|
687
690
|
.action(async (options) => {
|
|
688
691
|
requireTool('rpk', {
|
|
689
692
|
versionFlag: '--version',
|
|
@@ -744,7 +747,9 @@ automation
|
|
|
744
747
|
templateIntro: options.templateIntro,
|
|
745
748
|
templateFields: options.templateFields,
|
|
746
749
|
templateExamples: options.templateExamples,
|
|
747
|
-
|
|
750
|
+
templateBloblang: options.templateBloblang,
|
|
751
|
+
writeFullDrafts: false,
|
|
752
|
+
includeBloblang: !!options.includeBloblang
|
|
748
753
|
});
|
|
749
754
|
partialsWritten = result.partialsWritten;
|
|
750
755
|
partialFiles = result.partialFiles;
|
|
@@ -826,10 +831,13 @@ automation
|
|
|
826
831
|
}
|
|
827
832
|
|
|
828
833
|
let oldIndex = {};
|
|
834
|
+
let oldVersion = null;
|
|
829
835
|
if (options.oldData && fs.existsSync(options.oldData)) {
|
|
830
836
|
oldIndex = JSON.parse(fs.readFileSync(options.oldData, 'utf8'));
|
|
837
|
+
const m = options.oldData.match(/connect-([\d.]+)\.json$/);
|
|
838
|
+
if (m) oldVersion = m[1];
|
|
831
839
|
} else {
|
|
832
|
-
|
|
840
|
+
oldVersion = getAntoraValue('asciidoc.attributes.latest-connect-version');
|
|
833
841
|
if (oldVersion) {
|
|
834
842
|
const oldPath = path.join(dataDir, `connect-${oldVersion}.json`);
|
|
835
843
|
if (fs.existsSync(oldPath)) {
|
|
@@ -841,6 +849,21 @@ automation
|
|
|
841
849
|
const newIndex = JSON.parse(fs.readFileSync(dataFile, 'utf8'));
|
|
842
850
|
printDeltaReport(oldIndex, newIndex);
|
|
843
851
|
|
|
852
|
+
// Generate JSON diff file for whats-new.adoc
|
|
853
|
+
const { generateConnectorDiffJson } = require('../tools/redpanda-connect/report-delta.js');
|
|
854
|
+
const diffJson = generateConnectorDiffJson(
|
|
855
|
+
oldIndex,
|
|
856
|
+
newIndex,
|
|
857
|
+
{
|
|
858
|
+
oldVersion: oldVersion || '',
|
|
859
|
+
newVersion,
|
|
860
|
+
timestamp
|
|
861
|
+
}
|
|
862
|
+
);
|
|
863
|
+
const diffPath = path.join(dataDir, `connect-diff-${(oldVersion || 'unknown')}_to_${newVersion}.json`);
|
|
864
|
+
fs.writeFileSync(diffPath, JSON.stringify(diffJson, null, 2), 'utf8');
|
|
865
|
+
console.log(`✅ Connector diff JSON written to: ${diffPath}`);
|
|
866
|
+
|
|
844
867
|
function logCollapsed(label, filesArray, maxToShow = 10) {
|
|
845
868
|
console.log(` • ${label}: ${filesArray.length} total`);
|
|
846
869
|
const sample = filesArray.slice(0, maxToShow);
|
|
@@ -872,6 +895,135 @@ automation
|
|
|
872
895
|
logCollapsed('Draft files', draftFiles, 5);
|
|
873
896
|
}
|
|
874
897
|
|
|
898
|
+
// Optionally update whats-new.adoc
|
|
899
|
+
if (options.updateWhatsNew) {
|
|
900
|
+
try {
|
|
901
|
+
const whatsNewPath = path.join(findRepoRoot(), 'modules/get-started/pages/whats-new.adoc');
|
|
902
|
+
if (!fs.existsSync(whatsNewPath)) {
|
|
903
|
+
console.error(`❌ Unable to update release notes: 'whats-new.adoc' was not found at: ${whatsNewPath}\nPlease ensure this file exists and is tracked in your repository.`);
|
|
904
|
+
return;
|
|
905
|
+
}
|
|
906
|
+
// Find the diff JSON file we just wrote
|
|
907
|
+
const diffPath = path.join(dataDir, `connect-diff-${(oldVersion || 'unknown')}_to_${newVersion}.json`);
|
|
908
|
+
if (!fs.existsSync(diffPath)) {
|
|
909
|
+
console.error(`❌ Unable to update release notes: The connector diff JSON was not found at: ${diffPath}\nPlease ensure the diff was generated successfully before updating release notes.`);
|
|
910
|
+
return;
|
|
911
|
+
}
|
|
912
|
+
let diff;
|
|
913
|
+
try {
|
|
914
|
+
diff = JSON.parse(fs.readFileSync(diffPath, 'utf8'));
|
|
915
|
+
} catch (jsonErr) {
|
|
916
|
+
console.error(`❌ Unable to parse connector diff JSON at ${diffPath}: ${jsonErr.message}\nPlease check the file for syntax errors or corruption.`);
|
|
917
|
+
return;
|
|
918
|
+
}
|
|
919
|
+
let whatsNewContent;
|
|
920
|
+
try {
|
|
921
|
+
whatsNewContent = fs.readFileSync(whatsNewPath, 'utf8');
|
|
922
|
+
} catch (readErr) {
|
|
923
|
+
console.error(`❌ Unable to read whats-new.adoc at ${whatsNewPath}: ${readErr.message}\nPlease check file permissions and try again.`);
|
|
924
|
+
return;
|
|
925
|
+
}
|
|
926
|
+
const whatsNew = whatsNewContent;
|
|
927
|
+
// Regex to find section for this version
|
|
928
|
+
const versionTitle = `== Version ${diff.comparison.newVersion}`;
|
|
929
|
+
const versionRe = new RegExp(`^== Version ${diff.comparison.newVersion.replace(/[-.]/g, '\\$&')}(?:\\r?\\n|$)`, 'm');
|
|
930
|
+
const match = versionRe.exec(whatsNew);
|
|
931
|
+
let startIdx = match ? match.index : -1;
|
|
932
|
+
let endIdx = -1;
|
|
933
|
+
if (startIdx !== -1) {
|
|
934
|
+
// Find the start of the next version section
|
|
935
|
+
const rest = whatsNew.slice(startIdx + 1);
|
|
936
|
+
const nextMatch = /^== Version /m.exec(rest);
|
|
937
|
+
endIdx = nextMatch ? startIdx + 1 + nextMatch.index : whatsNew.length;
|
|
938
|
+
}
|
|
939
|
+
// Compose new section
|
|
940
|
+
let section = `\n== Version ${diff.comparison.newVersion}\n\n=== Component updates\n\n`;
|
|
941
|
+
// Add link to full release notes for this connector version after version heading and before component updates
|
|
942
|
+
let releaseNotesLink = '';
|
|
943
|
+
if (diff.comparison && diff.comparison.newVersion) {
|
|
944
|
+
releaseNotesLink = `link:https://github.com/redpanda-data/connect/releases/tag/v${diff.comparison.newVersion}[See the full release notes^].\n\n`;
|
|
945
|
+
}
|
|
946
|
+
section = `\n== Version ${diff.comparison.newVersion}\n\n${releaseNotesLink}=== Component updates\n\n`;
|
|
947
|
+
// New components
|
|
948
|
+
if (diff.details.newComponents && diff.details.newComponents.length) {
|
|
949
|
+
section += 'This release adds the following new components:\n\n';
|
|
950
|
+
// Group by type
|
|
951
|
+
const byType = {};
|
|
952
|
+
for (const comp of diff.details.newComponents) {
|
|
953
|
+
if (!byType[comp.type]) byType[comp.type] = [];
|
|
954
|
+
byType[comp.type].push(comp);
|
|
955
|
+
}
|
|
956
|
+
for (const [type, comps] of Object.entries(byType)) {
|
|
957
|
+
section += `* ${type.charAt(0).toUpperCase() + type.slice(1)}:\n`;
|
|
958
|
+
for (const comp of comps) {
|
|
959
|
+
section += `** xref:components:${type}/${comp.name}.adoc[\`${comp.name}\`]`;
|
|
960
|
+
if (comp.status) section += ` (${comp.status})`;
|
|
961
|
+
if (comp.description) section += `: ${comp.description}`;
|
|
962
|
+
section += '\n';
|
|
963
|
+
}
|
|
964
|
+
}
|
|
965
|
+
}
|
|
966
|
+
|
|
967
|
+
// New fields
|
|
968
|
+
if (diff.details.newFields && diff.details.newFields.length) {
|
|
969
|
+
section += '\nThis release adds support for the following new fields:\n\n';
|
|
970
|
+
// Group new fields by component type
|
|
971
|
+
const fieldsByType = {};
|
|
972
|
+
for (const field of diff.details.newFields) {
|
|
973
|
+
// component: "inputs:kafka", field: "timely_nacks_maximum_wait"
|
|
974
|
+
const [type, compName] = field.component.split(':');
|
|
975
|
+
if (!fieldsByType[type]) fieldsByType[type] = [];
|
|
976
|
+
fieldsByType[type].push({
|
|
977
|
+
compName,
|
|
978
|
+
field: field.field,
|
|
979
|
+
description: field.description || '',
|
|
980
|
+
});
|
|
981
|
+
}
|
|
982
|
+
for (const [type, fields] of Object.entries(fieldsByType)) {
|
|
983
|
+
section += `* ${type.charAt(0).toUpperCase() + type.slice(1)} components\n`;
|
|
984
|
+
// Group by component name
|
|
985
|
+
const byComp = {};
|
|
986
|
+
for (const f of fields) {
|
|
987
|
+
if (!byComp[f.compName]) byComp[f.compName] = [];
|
|
988
|
+
byComp[f.compName].push(f);
|
|
989
|
+
}
|
|
990
|
+
for (const [comp, compFields] of Object.entries(byComp)) {
|
|
991
|
+
section += `** xref:components:${type}/${comp}.adoc['${comp}']`;
|
|
992
|
+
if (compFields.length === 1) {
|
|
993
|
+
const f = compFields[0];
|
|
994
|
+
section += `: xref:components:${type}/${comp}.adoc#${f.field}['${f.field}']`;
|
|
995
|
+
if (f.description) section += ` - ${f.description}`;
|
|
996
|
+
section += '\n';
|
|
997
|
+
} else {
|
|
998
|
+
section += '\n';
|
|
999
|
+
for (const f of compFields) {
|
|
1000
|
+
section += `*** xref:components:${type}/${comp}.adoc#${f.field}['${f.field}']`;
|
|
1001
|
+
if (f.description) section += ` - ${f.description}`;
|
|
1002
|
+
section += '\n';
|
|
1003
|
+
}
|
|
1004
|
+
}
|
|
1005
|
+
}
|
|
1006
|
+
}
|
|
1007
|
+
}
|
|
1008
|
+
let updated;
|
|
1009
|
+
if (startIdx !== -1) {
|
|
1010
|
+
// Replace the existing section
|
|
1011
|
+
updated = whatsNew.slice(0, startIdx) + section + '\n' + whatsNew.slice(endIdx);
|
|
1012
|
+
console.log(`♻️ whats-new.adoc: replaced section for Version ${diff.comparison.newVersion}`);
|
|
1013
|
+
} else {
|
|
1014
|
+
// Insert above first version heading
|
|
1015
|
+
const versionHeading = /^== Version /m;
|
|
1016
|
+
const firstMatch = versionHeading.exec(whatsNew);
|
|
1017
|
+
let insertIdx = firstMatch ? firstMatch.index : 0;
|
|
1018
|
+
updated = whatsNew.slice(0, insertIdx) + section + '\n' + whatsNew.slice(insertIdx);
|
|
1019
|
+
console.log(`✅ whats-new.adoc updated with Version ${diff.comparison.newVersion}`);
|
|
1020
|
+
}
|
|
1021
|
+
fs.writeFileSync(whatsNewPath, updated, 'utf8');
|
|
1022
|
+
} catch (err) {
|
|
1023
|
+
console.error(`❌ Failed to update whats-new.adoc: ${err.message}`);
|
|
1024
|
+
}
|
|
1025
|
+
}
|
|
1026
|
+
|
|
875
1027
|
console.log('\n📄 Summary:');
|
|
876
1028
|
console.log(` • Run time: ${timestamp}`);
|
|
877
1029
|
console.log(` • Version used: ${newVersion}`);
|
|
@@ -1388,5 +1540,47 @@ automation
|
|
|
1388
1540
|
}
|
|
1389
1541
|
});
|
|
1390
1542
|
|
|
1543
|
+
automation
|
|
1544
|
+
.command('bundle-openapi')
|
|
1545
|
+
.description('Bundle Redpanda OpenAPI fragments for admin and connect APIs into complete OpenAPI 3.1 documents')
|
|
1546
|
+
.requiredOption('-t, --tag <tag>', 'Git tag to check out (e.g., v24.3.2 or 24.3.2 or dev)')
|
|
1547
|
+
.option('--repo <url>', 'Repository URL', 'https://github.com/redpanda-data/redpanda.git')
|
|
1548
|
+
.addOption(new Option('-s, --surface <surface>', 'Which API surface(s) to bundle').choices(['admin', 'connect', 'both']).makeOptionMandatory())
|
|
1549
|
+
.option('--out-admin <path>', 'Output path for admin API', 'admin/redpanda-admin-api.yaml')
|
|
1550
|
+
.option('--out-connect <path>', 'Output path for connect API', 'connect/redpanda-connect-api.yaml')
|
|
1551
|
+
.option('--admin-major <string>', 'Admin API major version', 'v2.0.0')
|
|
1552
|
+
.option('--use-admin-major-version', 'Use admin major version for info.version instead of git tag', false)
|
|
1553
|
+
.option('--quiet', 'Suppress logs', false)
|
|
1554
|
+
.action(async (options) => {
|
|
1555
|
+
// Verify dependencies
|
|
1556
|
+
requireCmd('git', 'Install Git: https://git-scm.com/downloads');
|
|
1557
|
+
requireCmd('buf', 'buf should be automatically available after npm install');
|
|
1558
|
+
|
|
1559
|
+
// Check for OpenAPI bundler using the existing detectBundler function
|
|
1560
|
+
try {
|
|
1561
|
+
const { detectBundler } = require('../tools/bundle-openapi.js');
|
|
1562
|
+
detectBundler(true); // quiet mode to avoid duplicate output
|
|
1563
|
+
} catch (err) {
|
|
1564
|
+
fail(err.message);
|
|
1565
|
+
}
|
|
1566
|
+
|
|
1567
|
+
try {
|
|
1568
|
+
const { bundleOpenAPI } = require('../tools/bundle-openapi.js');
|
|
1569
|
+
await bundleOpenAPI({
|
|
1570
|
+
tag: options.tag,
|
|
1571
|
+
repo: options.repo,
|
|
1572
|
+
surface: options.surface,
|
|
1573
|
+
outAdmin: options.outAdmin,
|
|
1574
|
+
outConnect: options.outConnect,
|
|
1575
|
+
adminMajor: options.adminMajor,
|
|
1576
|
+
useAdminMajorVersion: options.useAdminMajorVersion,
|
|
1577
|
+
quiet: options.quiet
|
|
1578
|
+
});
|
|
1579
|
+
} catch (err) {
|
|
1580
|
+
console.error(`❌ ${err.message}`);
|
|
1581
|
+
process.exit(err.message.includes('Validation failed') ? 2 : 1);
|
|
1582
|
+
}
|
|
1583
|
+
});
|
|
1584
|
+
|
|
1391
1585
|
programCli.addCommand(automation);
|
|
1392
1586
|
programCli.parse(process.argv);
|