@redpanda-data/docs-extensions-and-macros 4.13.1 → 4.13.2
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 +15 -3
- package/bin/doc-tools.js +767 -2088
- package/bin/mcp-tools/property-docs.js +18 -0
- package/bin/mcp-tools/rpcn-docs.js +28 -3
- package/cli-utils/antora-utils.js +53 -2
- package/cli-utils/dependencies.js +313 -0
- package/cli-utils/diff-utils.js +273 -0
- package/cli-utils/doc-tools-utils.js +54 -0
- package/extensions/algolia-indexer/generate-index.js +134 -102
- package/extensions/algolia-indexer/index.js +70 -38
- package/extensions/collect-bloblang-samples.js +2 -1
- package/extensions/generate-rp-connect-categories.js +126 -67
- package/extensions/generate-rp-connect-info.js +291 -137
- package/macros/rp-connect-components.js +34 -5
- package/package.json +4 -3
- package/tools/add-commercial-names.js +207 -0
- package/tools/generate-cli-docs.js +6 -2
- package/tools/get-console-version.js +5 -0
- package/tools/get-redpanda-version.js +5 -0
- package/tools/property-extractor/compare-properties.js +3 -3
- package/tools/property-extractor/generate-handlebars-docs.js +14 -14
- package/tools/property-extractor/generate-pr-summary.js +46 -0
- package/tools/property-extractor/pr-summary-formatter.js +375 -0
- package/tools/redpanda-connect/README.adoc +403 -38
- package/tools/redpanda-connect/connector-binary-analyzer.js +588 -0
- package/tools/redpanda-connect/generate-rpcn-connector-docs.js +97 -34
- package/tools/redpanda-connect/parse-csv-connectors.js +1 -1
- package/tools/redpanda-connect/pr-summary-formatter.js +601 -0
- package/tools/redpanda-connect/report-delta.js +69 -2
- package/tools/redpanda-connect/rpcn-connector-docs-handler.js +1180 -0
- package/tools/redpanda-connect/templates/connector.hbs +38 -0
- package/tools/redpanda-connect/templates/intro.hbs +0 -20
- package/tools/redpanda-connect/update-nav.js +205 -0
|
@@ -0,0 +1,601 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Format diff and cloud support data into a PR-friendly summary
|
|
3
|
+
* Outputs a console-parseable format for GitHub Actions
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Generate a PR-friendly summary for connector changes
|
|
8
|
+
* @param {object} diffData - Diff data from generateConnectorDiffJson
|
|
9
|
+
* @param {object} binaryAnalysis - Cloud support data from getCloudSupport
|
|
10
|
+
* @param {array} draftedConnectors - Array of newly drafted connectors
|
|
11
|
+
* @returns {string} Formatted summary
|
|
12
|
+
*/
|
|
13
|
+
function generatePRSummary(diffData, binaryAnalysis = null, draftedConnectors = null) {
|
|
14
|
+
const lines = [];
|
|
15
|
+
|
|
16
|
+
// Header with delimiters for GitHub Action parsing
|
|
17
|
+
lines.push('<!-- PR_SUMMARY_START -->');
|
|
18
|
+
lines.push('');
|
|
19
|
+
|
|
20
|
+
// Quick Summary Section
|
|
21
|
+
lines.push('## 📊 Redpanda Connect Documentation Update');
|
|
22
|
+
lines.push('');
|
|
23
|
+
lines.push(`**OSS Version:** ${diffData.comparison.oldVersion} → ${diffData.comparison.newVersion}`);
|
|
24
|
+
|
|
25
|
+
if (binaryAnalysis) {
|
|
26
|
+
lines.push(`**Cloud Version:** ${binaryAnalysis.cloudVersion}`);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
lines.push('');
|
|
30
|
+
|
|
31
|
+
// High-level stats
|
|
32
|
+
const stats = diffData.summary;
|
|
33
|
+
const hasChanges = Object.values(stats).some(v => v > 0);
|
|
34
|
+
|
|
35
|
+
if (!hasChanges) {
|
|
36
|
+
lines.push('✅ **No changes detected** - Documentation is up to date');
|
|
37
|
+
lines.push('');
|
|
38
|
+
lines.push('<!-- PR_SUMMARY_END -->');
|
|
39
|
+
return lines.join('\n');
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
lines.push('### Summary');
|
|
43
|
+
lines.push('');
|
|
44
|
+
|
|
45
|
+
if (stats.newComponents > 0) {
|
|
46
|
+
lines.push(`- **${stats.newComponents}** new connector${stats.newComponents !== 1 ? 's' : ''}`);
|
|
47
|
+
|
|
48
|
+
if (binaryAnalysis) {
|
|
49
|
+
const newConnectorKeys = diffData.details.newComponents.map(c => `${c.type}:${c.name}`);
|
|
50
|
+
const cloudSupported = newConnectorKeys.filter(key => {
|
|
51
|
+
const inCloud = binaryAnalysis.comparison.inCloud.some(c => `${c.type}:${c.name}` === key);
|
|
52
|
+
return inCloud;
|
|
53
|
+
}).length;
|
|
54
|
+
|
|
55
|
+
const needsCloudDocs = cloudSupported;
|
|
56
|
+
|
|
57
|
+
if (needsCloudDocs > 0) {
|
|
58
|
+
lines.push(` - ${needsCloudDocs} need${needsCloudDocs !== 1 ? '' : 's'} cloud docs ☁️`);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (stats.newFields > 0) {
|
|
64
|
+
const affectedComponents = new Set(diffData.details.newFields.map(f => f.component)).size;
|
|
65
|
+
lines.push(`- **${stats.newFields}** new field${stats.newFields !== 1 ? 's' : ''} across ${affectedComponents} connector${affectedComponents !== 1 ? 's' : ''}`);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (stats.removedComponents > 0) {
|
|
69
|
+
lines.push(`- **${stats.removedComponents}** removed connector${stats.removedComponents !== 1 ? 's' : ''} ⚠️`);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (stats.removedFields > 0) {
|
|
73
|
+
lines.push(`- **${stats.removedFields}** removed field${stats.removedFields !== 1 ? 's' : ''} ⚠️`);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
if (stats.deprecatedComponents > 0) {
|
|
77
|
+
lines.push(`- **${stats.deprecatedComponents}** deprecated connector${stats.deprecatedComponents !== 1 ? 's' : ''}`);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if (stats.deprecatedFields > 0) {
|
|
81
|
+
lines.push(`- **${stats.deprecatedFields}** deprecated field${stats.deprecatedFields !== 1 ? 's' : ''}`);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
if (stats.changedDefaults > 0) {
|
|
85
|
+
lines.push(`- **${stats.changedDefaults}** default value change${stats.changedDefaults !== 1 ? 's' : ''} ⚠️`);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
lines.push('');
|
|
89
|
+
|
|
90
|
+
// Writer Reminder for Commercial Names
|
|
91
|
+
if (stats.newComponents > 0) {
|
|
92
|
+
lines.push('### ✍️ Writer Action Required');
|
|
93
|
+
lines.push('');
|
|
94
|
+
lines.push('For each new connector, please add the `:commercial-names:` attribute to the frontmatter:');
|
|
95
|
+
lines.push('');
|
|
96
|
+
lines.push('```asciidoc');
|
|
97
|
+
lines.push('= Connector Name');
|
|
98
|
+
lines.push(':type: input');
|
|
99
|
+
lines.push(':commercial-names: Commercial Name, Alternative Name');
|
|
100
|
+
lines.push('```');
|
|
101
|
+
lines.push('');
|
|
102
|
+
lines.push('_This helps improve discoverability and ensures proper categorization._');
|
|
103
|
+
lines.push('');
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Breaking Changes Section
|
|
107
|
+
const breakingChanges = [];
|
|
108
|
+
if (stats.removedComponents > 0) breakingChanges.push('removed connectors');
|
|
109
|
+
if (stats.removedFields > 0) breakingChanges.push('removed fields');
|
|
110
|
+
if (stats.changedDefaults > 0) breakingChanges.push('changed defaults');
|
|
111
|
+
|
|
112
|
+
if (breakingChanges.length > 0) {
|
|
113
|
+
lines.push('### ⚠️ Breaking Changes Detected');
|
|
114
|
+
lines.push('');
|
|
115
|
+
lines.push(`This update includes **${breakingChanges.join(', ')}** that may affect existing configurations.`);
|
|
116
|
+
lines.push('');
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// Newly Drafted Connectors Section
|
|
120
|
+
if (draftedConnectors && draftedConnectors.length > 0) {
|
|
121
|
+
lines.push('### 📝 Newly Drafted - Needs Review');
|
|
122
|
+
lines.push('');
|
|
123
|
+
lines.push(`**${draftedConnectors.length}** connector${draftedConnectors.length !== 1 ? 's have' : ' has'} been auto-generated and placed in the proper location. These drafts need writer review:`);
|
|
124
|
+
lines.push('');
|
|
125
|
+
|
|
126
|
+
// Group by type
|
|
127
|
+
const draftsByType = {};
|
|
128
|
+
draftedConnectors.forEach(draft => {
|
|
129
|
+
const type = draft.type || 'unknown';
|
|
130
|
+
if (!draftsByType[type]) {
|
|
131
|
+
draftsByType[type] = [];
|
|
132
|
+
}
|
|
133
|
+
draftsByType[type].push(draft);
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
// List drafts by type
|
|
137
|
+
Object.entries(draftsByType).forEach(([type, drafts]) => {
|
|
138
|
+
lines.push(`**${type}:**`);
|
|
139
|
+
drafts.forEach(draft => {
|
|
140
|
+
const cloudIndicator = binaryAnalysis?.comparison.inCloud.some(c =>
|
|
141
|
+
c.type === type && c.name === draft.name
|
|
142
|
+
) ? ' ☁️' : '';
|
|
143
|
+
const cgoIndicator = draft.requiresCgo ? ' 🔧' : '';
|
|
144
|
+
const statusBadge = draft.status && draft.status !== 'stable' ? ` (${draft.status})` : '';
|
|
145
|
+
lines.push(`- \`${draft.name}\`${statusBadge}${cloudIndicator}${cgoIndicator} → \`${draft.path}\``);
|
|
146
|
+
});
|
|
147
|
+
lines.push('');
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// Missing Descriptions Warning
|
|
152
|
+
const missingDescriptions = [];
|
|
153
|
+
|
|
154
|
+
// Check for new components with missing descriptions
|
|
155
|
+
if (stats.newComponents > 0) {
|
|
156
|
+
diffData.details.newComponents.forEach(connector => {
|
|
157
|
+
if (!connector.description || connector.description.trim() === '') {
|
|
158
|
+
missingDescriptions.push({
|
|
159
|
+
type: 'component',
|
|
160
|
+
name: connector.name,
|
|
161
|
+
componentType: connector.type
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// Check for new fields with missing descriptions
|
|
168
|
+
if (stats.newFields > 0) {
|
|
169
|
+
diffData.details.newFields.forEach(field => {
|
|
170
|
+
if (!field.description || field.description.trim() === '') {
|
|
171
|
+
missingDescriptions.push({
|
|
172
|
+
type: 'field',
|
|
173
|
+
name: field.field,
|
|
174
|
+
component: field.component
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
if (missingDescriptions.length > 0) {
|
|
181
|
+
lines.push('### ⚠️ Missing Descriptions');
|
|
182
|
+
lines.push('');
|
|
183
|
+
lines.push(`**${missingDescriptions.length}** item${missingDescriptions.length !== 1 ? 's' : ''} missing descriptions - these need writer attention:`);
|
|
184
|
+
lines.push('');
|
|
185
|
+
|
|
186
|
+
const componentsMissing = missingDescriptions.filter(m => m.type === 'component');
|
|
187
|
+
const fieldsMissing = missingDescriptions.filter(m => m.type === 'field');
|
|
188
|
+
|
|
189
|
+
if (componentsMissing.length > 0) {
|
|
190
|
+
lines.push('**Components:**');
|
|
191
|
+
componentsMissing.forEach(m => {
|
|
192
|
+
lines.push(`- \`${m.name}\` (${m.componentType})`);
|
|
193
|
+
});
|
|
194
|
+
lines.push('');
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
if (fieldsMissing.length > 0) {
|
|
198
|
+
lines.push('**Fields:**');
|
|
199
|
+
// Group by component
|
|
200
|
+
const fieldsByComponent = {};
|
|
201
|
+
fieldsMissing.forEach(m => {
|
|
202
|
+
if (!fieldsByComponent[m.component]) {
|
|
203
|
+
fieldsByComponent[m.component] = [];
|
|
204
|
+
}
|
|
205
|
+
fieldsByComponent[m.component].push(m.name);
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
Object.entries(fieldsByComponent).forEach(([component, fields]) => {
|
|
209
|
+
const [type, name] = component.split(':');
|
|
210
|
+
lines.push(`- **${type}/${name}:** ${fields.map(f => `\`${f}\``).join(', ')}`);
|
|
211
|
+
});
|
|
212
|
+
lines.push('');
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// Action Items
|
|
217
|
+
lines.push('### 📝 Action Items for Writers');
|
|
218
|
+
lines.push('');
|
|
219
|
+
|
|
220
|
+
const actionItems = [];
|
|
221
|
+
|
|
222
|
+
// Add action items for missing descriptions
|
|
223
|
+
if (missingDescriptions.length > 0) {
|
|
224
|
+
actionItems.push({
|
|
225
|
+
priority: 0,
|
|
226
|
+
text: `⚠️ Add descriptions for ${missingDescriptions.length} component${missingDescriptions.length !== 1 ? 's' : ''}/field${missingDescriptions.length !== 1 ? 's' : ''} (see Missing Descriptions section)`
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// New connectors that need cloud docs
|
|
231
|
+
if (binaryAnalysis && stats.newComponents > 0) {
|
|
232
|
+
diffData.details.newComponents.forEach(connector => {
|
|
233
|
+
const key = `${connector.type}:${connector.name}`;
|
|
234
|
+
const inCloud = binaryAnalysis.comparison.inCloud.some(c => `${c.type}:${c.name}` === key);
|
|
235
|
+
|
|
236
|
+
if (inCloud) {
|
|
237
|
+
actionItems.push({
|
|
238
|
+
priority: 1,
|
|
239
|
+
text: `Document new \`${connector.name}\` ${connector.type} (☁️ **CLOUD SUPPORTED**)`
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
// New connectors without cloud support
|
|
246
|
+
if (stats.newComponents > 0) {
|
|
247
|
+
diffData.details.newComponents.forEach(connector => {
|
|
248
|
+
const key = `${connector.type}:${connector.name}`;
|
|
249
|
+
const inCloud = binaryAnalysis?.comparison.inCloud.some(c => `${c.type}:${c.name}` === key);
|
|
250
|
+
|
|
251
|
+
if (!inCloud) {
|
|
252
|
+
actionItems.push({
|
|
253
|
+
priority: 2,
|
|
254
|
+
text: `Document new \`${connector.name}\` ${connector.type} (self-hosted only)`
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
// Deprecated connectors
|
|
261
|
+
if (stats.deprecatedComponents > 0) {
|
|
262
|
+
diffData.details.deprecatedComponents.forEach(connector => {
|
|
263
|
+
actionItems.push({
|
|
264
|
+
priority: 3,
|
|
265
|
+
text: `Update docs for deprecated \`${connector.name}\` ${connector.type}`
|
|
266
|
+
});
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
// Removed connectors
|
|
271
|
+
if (stats.removedComponents > 0) {
|
|
272
|
+
diffData.details.removedComponents.forEach(connector => {
|
|
273
|
+
actionItems.push({
|
|
274
|
+
priority: 3,
|
|
275
|
+
text: `Update migration guide for removed \`${connector.name}\` ${connector.type}`
|
|
276
|
+
});
|
|
277
|
+
});
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
// Changed defaults that may break configs
|
|
281
|
+
if (stats.changedDefaults > 0) {
|
|
282
|
+
actionItems.push({
|
|
283
|
+
priority: 4,
|
|
284
|
+
text: `Review ${stats.changedDefaults} default value change${stats.changedDefaults !== 1 ? 's' : ''} for breaking changes`
|
|
285
|
+
});
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
// Sort by priority and output
|
|
289
|
+
actionItems.sort((a, b) => a.priority - b.priority);
|
|
290
|
+
|
|
291
|
+
if (actionItems.length > 0) {
|
|
292
|
+
actionItems.forEach(item => {
|
|
293
|
+
lines.push(`- [ ] ${item.text}`);
|
|
294
|
+
});
|
|
295
|
+
} else {
|
|
296
|
+
lines.push('- [ ] Review generated documentation');
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
lines.push('');
|
|
300
|
+
|
|
301
|
+
// Detailed breakdown (expandable)
|
|
302
|
+
lines.push('<details>');
|
|
303
|
+
lines.push('<summary><strong>📋 Detailed Changes</strong> (click to expand)</summary>');
|
|
304
|
+
lines.push('');
|
|
305
|
+
|
|
306
|
+
// New Connectors
|
|
307
|
+
if (stats.newComponents > 0) {
|
|
308
|
+
lines.push('#### New Connectors');
|
|
309
|
+
lines.push('');
|
|
310
|
+
|
|
311
|
+
if (binaryAnalysis) {
|
|
312
|
+
const cloudSupportedNew = [];
|
|
313
|
+
const selfHostedOnlyNew = [];
|
|
314
|
+
|
|
315
|
+
diffData.details.newComponents.forEach(connector => {
|
|
316
|
+
const key = `${connector.type}:${connector.name}`;
|
|
317
|
+
const inCloud = binaryAnalysis.comparison.inCloud.some(c => `${c.type}:${c.name}` === key);
|
|
318
|
+
|
|
319
|
+
const entry = {
|
|
320
|
+
name: connector.name,
|
|
321
|
+
type: connector.type,
|
|
322
|
+
status: connector.status,
|
|
323
|
+
description: connector.description
|
|
324
|
+
};
|
|
325
|
+
|
|
326
|
+
if (inCloud) {
|
|
327
|
+
cloudSupportedNew.push(entry);
|
|
328
|
+
} else {
|
|
329
|
+
selfHostedOnlyNew.push(entry);
|
|
330
|
+
}
|
|
331
|
+
});
|
|
332
|
+
|
|
333
|
+
if (cloudSupportedNew.length > 0) {
|
|
334
|
+
lines.push('**☁️ Cloud Supported:**');
|
|
335
|
+
lines.push('');
|
|
336
|
+
cloudSupportedNew.forEach(c => {
|
|
337
|
+
lines.push(`- **${c.name}** (${c.type}, ${c.status})`);
|
|
338
|
+
if (c.description) {
|
|
339
|
+
const shortDesc = truncateToSentence(c.description, 2);
|
|
340
|
+
lines.push(` - ${shortDesc}`);
|
|
341
|
+
}
|
|
342
|
+
});
|
|
343
|
+
lines.push('');
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
if (selfHostedOnlyNew.length > 0) {
|
|
347
|
+
lines.push('**Self-Hosted Only:**');
|
|
348
|
+
lines.push('');
|
|
349
|
+
selfHostedOnlyNew.forEach(c => {
|
|
350
|
+
lines.push(`- **${c.name}** (${c.type}, ${c.status})`);
|
|
351
|
+
if (c.description) {
|
|
352
|
+
const shortDesc = truncateToSentence(c.description, 2);
|
|
353
|
+
lines.push(` - ${shortDesc}`);
|
|
354
|
+
}
|
|
355
|
+
});
|
|
356
|
+
lines.push('');
|
|
357
|
+
}
|
|
358
|
+
} else {
|
|
359
|
+
// No cloud support info, just list all
|
|
360
|
+
diffData.details.newComponents.forEach(c => {
|
|
361
|
+
lines.push(`- **${c.name}** (${c.type}, ${c.status})`);
|
|
362
|
+
if (c.description) {
|
|
363
|
+
const shortDesc = truncateToSentence(c.description, 2);
|
|
364
|
+
lines.push(` - ${shortDesc}`);
|
|
365
|
+
}
|
|
366
|
+
});
|
|
367
|
+
lines.push('');
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
// cgo-only connectors (if any new connectors require cgo)
|
|
372
|
+
if (binaryAnalysis && binaryAnalysis.cgoOnly && binaryAnalysis.cgoOnly.length > 0 && stats.newComponents > 0) {
|
|
373
|
+
// Find new connectors that are cgo-only
|
|
374
|
+
const newCgoConnectors = diffData.details.newComponents.filter(connector => {
|
|
375
|
+
const key = `${connector.type}:${connector.name}`;
|
|
376
|
+
return binaryAnalysis.cgoOnly.some(cgo => `${cgo.type}:${cgo.name}` === key);
|
|
377
|
+
});
|
|
378
|
+
|
|
379
|
+
if (newCgoConnectors.length > 0) {
|
|
380
|
+
lines.push('#### 🔧 Cgo Requirements');
|
|
381
|
+
lines.push('');
|
|
382
|
+
lines.push('The following new connectors require cgo-enabled builds:');
|
|
383
|
+
lines.push('');
|
|
384
|
+
|
|
385
|
+
newCgoConnectors.forEach(connector => {
|
|
386
|
+
// Convert type to singular form for better grammar (e.g., "inputs" -> "input")
|
|
387
|
+
const typeSingular = connector.type.endsWith('s') ? connector.type.slice(0, -1) : connector.type;
|
|
388
|
+
|
|
389
|
+
lines.push(`**${connector.name}** (${connector.type}):`);
|
|
390
|
+
lines.push('');
|
|
391
|
+
lines.push('[NOTE]');
|
|
392
|
+
lines.push('====');
|
|
393
|
+
lines.push(`The \`${connector.name}\` ${typeSingular} requires a cgo-enabled build of Redpanda Connect.`);
|
|
394
|
+
lines.push('');
|
|
395
|
+
lines.push('For instructions, see:');
|
|
396
|
+
lines.push('');
|
|
397
|
+
lines.push('* xref:install:prebuilt-binary.adoc[Download a cgo-enabled binary]');
|
|
398
|
+
lines.push('* xref:install:build-from-source.adoc[Build Redpanda Connect from source]');
|
|
399
|
+
lines.push('====');
|
|
400
|
+
lines.push('');
|
|
401
|
+
});
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
// New Fields
|
|
406
|
+
if (stats.newFields > 0) {
|
|
407
|
+
lines.push('#### New Fields');
|
|
408
|
+
lines.push('');
|
|
409
|
+
|
|
410
|
+
// Group by component
|
|
411
|
+
const fieldsByComponent = {};
|
|
412
|
+
diffData.details.newFields.forEach(field => {
|
|
413
|
+
if (!fieldsByComponent[field.component]) {
|
|
414
|
+
fieldsByComponent[field.component] = [];
|
|
415
|
+
}
|
|
416
|
+
fieldsByComponent[field.component].push(field);
|
|
417
|
+
});
|
|
418
|
+
|
|
419
|
+
Object.entries(fieldsByComponent).forEach(([component, fields]) => {
|
|
420
|
+
const [type, name] = component.split(':');
|
|
421
|
+
lines.push(`**${type}/${name}:**`);
|
|
422
|
+
fields.forEach(f => {
|
|
423
|
+
lines.push(`- \`${f.field}\`${f.introducedIn ? ` (since ${f.introducedIn})` : ''}`);
|
|
424
|
+
});
|
|
425
|
+
lines.push('');
|
|
426
|
+
});
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
// Removed Connectors
|
|
430
|
+
if (stats.removedComponents > 0) {
|
|
431
|
+
lines.push('#### ⚠️ Removed Connectors');
|
|
432
|
+
lines.push('');
|
|
433
|
+
diffData.details.removedComponents.forEach(c => {
|
|
434
|
+
lines.push(`- **${c.name}** (${c.type})`);
|
|
435
|
+
});
|
|
436
|
+
lines.push('');
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
// Removed Fields
|
|
440
|
+
if (stats.removedFields > 0) {
|
|
441
|
+
lines.push('#### ⚠️ Removed Fields');
|
|
442
|
+
lines.push('');
|
|
443
|
+
|
|
444
|
+
const fieldsByComponent = {};
|
|
445
|
+
diffData.details.removedFields.forEach(field => {
|
|
446
|
+
if (!fieldsByComponent[field.component]) {
|
|
447
|
+
fieldsByComponent[field.component] = [];
|
|
448
|
+
}
|
|
449
|
+
fieldsByComponent[field.component].push(field);
|
|
450
|
+
});
|
|
451
|
+
|
|
452
|
+
Object.entries(fieldsByComponent).forEach(([component, fields]) => {
|
|
453
|
+
const [type, name] = component.split(':');
|
|
454
|
+
lines.push(`**${type}/${name}:**`);
|
|
455
|
+
fields.forEach(f => {
|
|
456
|
+
lines.push(`- \`${f.field}\``);
|
|
457
|
+
});
|
|
458
|
+
lines.push('');
|
|
459
|
+
});
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
// Deprecated Connectors
|
|
463
|
+
if (stats.deprecatedComponents > 0) {
|
|
464
|
+
lines.push('#### Deprecated Connectors');
|
|
465
|
+
lines.push('');
|
|
466
|
+
diffData.details.deprecatedComponents.forEach(c => {
|
|
467
|
+
lines.push(`- **${c.name}** (${c.type})`);
|
|
468
|
+
});
|
|
469
|
+
lines.push('');
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
// Deprecated Fields
|
|
473
|
+
if (stats.deprecatedFields > 0) {
|
|
474
|
+
lines.push('#### Deprecated Fields');
|
|
475
|
+
lines.push('');
|
|
476
|
+
|
|
477
|
+
const fieldsByComponent = {};
|
|
478
|
+
diffData.details.deprecatedFields.forEach(field => {
|
|
479
|
+
if (!fieldsByComponent[field.component]) {
|
|
480
|
+
fieldsByComponent[field.component] = [];
|
|
481
|
+
}
|
|
482
|
+
fieldsByComponent[field.component].push(field);
|
|
483
|
+
});
|
|
484
|
+
|
|
485
|
+
Object.entries(fieldsByComponent).forEach(([component, fields]) => {
|
|
486
|
+
const [type, name] = component.split(':');
|
|
487
|
+
lines.push(`**${type}/${name}:**`);
|
|
488
|
+
fields.forEach(f => {
|
|
489
|
+
lines.push(`- \`${f.field}\``);
|
|
490
|
+
});
|
|
491
|
+
lines.push('');
|
|
492
|
+
});
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
// Changed Defaults
|
|
496
|
+
if (stats.changedDefaults > 0) {
|
|
497
|
+
lines.push('#### ⚠️ Changed Default Values');
|
|
498
|
+
lines.push('');
|
|
499
|
+
|
|
500
|
+
const changesByComponent = {};
|
|
501
|
+
diffData.details.changedDefaults.forEach(change => {
|
|
502
|
+
if (!changesByComponent[change.component]) {
|
|
503
|
+
changesByComponent[change.component] = [];
|
|
504
|
+
}
|
|
505
|
+
changesByComponent[change.component].push(change);
|
|
506
|
+
});
|
|
507
|
+
|
|
508
|
+
Object.entries(changesByComponent).forEach(([component, changes]) => {
|
|
509
|
+
const [type, name] = component.split(':');
|
|
510
|
+
lines.push(`**${type}/${name}:**`);
|
|
511
|
+
changes.forEach(c => {
|
|
512
|
+
const oldStr = JSON.stringify(c.oldDefault);
|
|
513
|
+
const newStr = JSON.stringify(c.newDefault);
|
|
514
|
+
lines.push(`- \`${c.field}\`: ${oldStr} → ${newStr}`);
|
|
515
|
+
});
|
|
516
|
+
lines.push('');
|
|
517
|
+
});
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
// Cloud Support Gap Analysis
|
|
521
|
+
if (binaryAnalysis && binaryAnalysis.comparison.notInCloud.length > 0) {
|
|
522
|
+
lines.push('#### 🔍 Cloud Support Gap Analysis');
|
|
523
|
+
lines.push('');
|
|
524
|
+
lines.push(`**${binaryAnalysis.comparison.notInCloud.length} connector${binaryAnalysis.comparison.notInCloud.length !== 1 ? 's' : ''} available in OSS but not in cloud:**`);
|
|
525
|
+
lines.push('');
|
|
526
|
+
|
|
527
|
+
// Group by type
|
|
528
|
+
const gapsByType = {};
|
|
529
|
+
binaryAnalysis.comparison.notInCloud.forEach(connector => {
|
|
530
|
+
if (!gapsByType[connector.type]) {
|
|
531
|
+
gapsByType[connector.type] = [];
|
|
532
|
+
}
|
|
533
|
+
gapsByType[connector.type].push(connector);
|
|
534
|
+
});
|
|
535
|
+
|
|
536
|
+
Object.entries(gapsByType).forEach(([type, connectors]) => {
|
|
537
|
+
lines.push(`**${type}:**`);
|
|
538
|
+
connectors.forEach(c => {
|
|
539
|
+
lines.push(`- ${c.name} (${c.status})`);
|
|
540
|
+
});
|
|
541
|
+
lines.push('');
|
|
542
|
+
});
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
lines.push('</details>');
|
|
546
|
+
lines.push('');
|
|
547
|
+
|
|
548
|
+
// Footer
|
|
549
|
+
lines.push('---');
|
|
550
|
+
lines.push('');
|
|
551
|
+
lines.push(`*Generated: ${diffData.comparison.timestamp}*`);
|
|
552
|
+
lines.push('');
|
|
553
|
+
lines.push('<!-- PR_SUMMARY_END -->');
|
|
554
|
+
|
|
555
|
+
return lines.join('\n');
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
/**
|
|
559
|
+
* Truncate description to specified number of sentences
|
|
560
|
+
* @param {string} text - Text to truncate
|
|
561
|
+
* @param {number} sentences - Number of sentences to keep
|
|
562
|
+
* @returns {string} Truncated text
|
|
563
|
+
*/
|
|
564
|
+
function truncateToSentence(text, sentences = 2) {
|
|
565
|
+
if (!text) return '';
|
|
566
|
+
|
|
567
|
+
// Remove markdown formatting
|
|
568
|
+
let clean = text
|
|
569
|
+
.replace(/\[([^\]]+)\]\([^\)]+\)/g, '$1') // Remove links
|
|
570
|
+
.replace(/[*_`]/g, '') // Remove emphasis
|
|
571
|
+
.replace(/\n/g, ' '); // Replace newlines with spaces
|
|
572
|
+
|
|
573
|
+
// Split by sentence boundaries
|
|
574
|
+
const sentenceRegex = /[^.!?]+[.!?]+/g;
|
|
575
|
+
const matches = clean.match(sentenceRegex);
|
|
576
|
+
|
|
577
|
+
if (!matches || matches.length === 0) {
|
|
578
|
+
return clean.substring(0, 150);
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
const truncated = matches.slice(0, sentences).join(' ').trim();
|
|
582
|
+
|
|
583
|
+
return truncated.length > 200 ? truncated.substring(0, 200) + '...' : truncated;
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
/**
|
|
587
|
+
* Print the PR summary to console
|
|
588
|
+
* @param {object} diffData - Diff data
|
|
589
|
+
* @param {object} binaryAnalysis - Cloud support data
|
|
590
|
+
* @param {array} draftedConnectors - Array of newly drafted connectors
|
|
591
|
+
*/
|
|
592
|
+
function printPRSummary(diffData, binaryAnalysis = null, draftedConnectors = null) {
|
|
593
|
+
const summary = generatePRSummary(diffData, binaryAnalysis, draftedConnectors);
|
|
594
|
+
console.log('\n' + summary + '\n');
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
module.exports = {
|
|
598
|
+
generatePRSummary,
|
|
599
|
+
printPRSummary,
|
|
600
|
+
truncateToSentence
|
|
601
|
+
};
|
|
@@ -4,7 +4,7 @@ const { execSync } = require('child_process');
|
|
|
4
4
|
* Generate a JSON diff report between two connector index objects.
|
|
5
5
|
* @param {object} oldIndex - Previous version connector index
|
|
6
6
|
* @param {object} newIndex - Current version connector index
|
|
7
|
-
* @param {object} opts - { oldVersion, newVersion, timestamp }
|
|
7
|
+
* @param {object} opts - { oldVersion, newVersion, timestamp, binaryAnalysis, oldBinaryAnalysis }
|
|
8
8
|
* @returns {object} JSON diff report
|
|
9
9
|
*/
|
|
10
10
|
function generateConnectorDiffJson(oldIndex, newIndex, opts = {}) {
|
|
@@ -163,7 +163,7 @@ function generateConnectorDiffJson(oldIndex, newIndex, opts = {}) {
|
|
|
163
163
|
});
|
|
164
164
|
});
|
|
165
165
|
|
|
166
|
-
|
|
166
|
+
const result = {
|
|
167
167
|
comparison: {
|
|
168
168
|
oldVersion: opts.oldVersion || '',
|
|
169
169
|
newVersion: opts.newVersion || '',
|
|
@@ -188,6 +188,73 @@ function generateConnectorDiffJson(oldIndex, newIndex, opts = {}) {
|
|
|
188
188
|
changedDefaults
|
|
189
189
|
}
|
|
190
190
|
};
|
|
191
|
+
|
|
192
|
+
// Include binary analysis data if provided
|
|
193
|
+
if (opts.binaryAnalysis) {
|
|
194
|
+
const ba = opts.binaryAnalysis;
|
|
195
|
+
const oldBa = opts.oldBinaryAnalysis || {};
|
|
196
|
+
|
|
197
|
+
result.binaryAnalysis = {
|
|
198
|
+
versions: {
|
|
199
|
+
oss: ba.ossVersion,
|
|
200
|
+
cloud: ba.cloudVersion || null,
|
|
201
|
+
cgo: ba.cgoVersion || null
|
|
202
|
+
},
|
|
203
|
+
current: {
|
|
204
|
+
cloudSupported: ba.comparison?.inCloud?.length || 0,
|
|
205
|
+
selfHostedOnly: ba.comparison?.notInCloud?.length || 0,
|
|
206
|
+
cgoOnly: ba.cgoOnly?.length || 0
|
|
207
|
+
},
|
|
208
|
+
changes: {}
|
|
209
|
+
};
|
|
210
|
+
|
|
211
|
+
// Calculate cloud support changes
|
|
212
|
+
if (oldBa.comparison && ba.comparison) {
|
|
213
|
+
const oldCloudSet = new Set(oldBa.comparison.inCloud?.map(c => `${c.type}:${c.name}`) || []);
|
|
214
|
+
const newCloudSet = new Set(ba.comparison.inCloud?.map(c => `${c.type}:${c.name}`) || []);
|
|
215
|
+
|
|
216
|
+
const addedToCloud = ba.comparison.inCloud?.filter(c =>
|
|
217
|
+
!oldCloudSet.has(`${c.type}:${c.name}`)
|
|
218
|
+
) || [];
|
|
219
|
+
|
|
220
|
+
const removedFromCloud = oldBa.comparison.inCloud?.filter(c =>
|
|
221
|
+
!newCloudSet.has(`${c.type}:${c.name}`)
|
|
222
|
+
) || [];
|
|
223
|
+
|
|
224
|
+
result.binaryAnalysis.changes.cloud = {
|
|
225
|
+
added: addedToCloud.map(c => ({ type: c.type, name: c.name, status: c.status })),
|
|
226
|
+
removed: removedFromCloud.map(c => ({ type: c.type, name: c.name, status: c.status }))
|
|
227
|
+
};
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// Calculate cgo-only changes
|
|
231
|
+
if (oldBa.cgoOnly && ba.cgoOnly) {
|
|
232
|
+
const oldCgoSet = new Set(oldBa.cgoOnly.map(c => `${c.type}:${c.name}`));
|
|
233
|
+
const newCgoSet = new Set(ba.cgoOnly.map(c => `${c.type}:${c.name}`));
|
|
234
|
+
|
|
235
|
+
const newCgoOnly = ba.cgoOnly.filter(c =>
|
|
236
|
+
!oldCgoSet.has(`${c.type}:${c.name}`)
|
|
237
|
+
);
|
|
238
|
+
|
|
239
|
+
const removedCgoOnly = oldBa.cgoOnly.filter(c =>
|
|
240
|
+
!newCgoSet.has(`${c.type}:${c.name}`)
|
|
241
|
+
);
|
|
242
|
+
|
|
243
|
+
result.binaryAnalysis.changes.cgo = {
|
|
244
|
+
newCgoOnly: newCgoOnly.map(c => ({ type: c.type, name: c.name, status: c.status })),
|
|
245
|
+
removedCgoOnly: removedCgoOnly.map(c => ({ type: c.type, name: c.name, status: c.status }))
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
// Include full lists for reference
|
|
250
|
+
result.binaryAnalysis.details = {
|
|
251
|
+
cloudSupported: ba.comparison?.inCloud?.map(c => ({ type: c.type, name: c.name, status: c.status })) || [],
|
|
252
|
+
selfHostedOnly: ba.comparison?.notInCloud?.map(c => ({ type: c.type, name: c.name, status: c.status })) || [],
|
|
253
|
+
cgoOnly: ba.cgoOnly?.map(c => ({ type: c.type, name: c.name, status: c.status })) || []
|
|
254
|
+
};
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
return result;
|
|
191
258
|
}
|
|
192
259
|
|
|
193
260
|
function discoverComponentKeys(obj) {
|