@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,588 @@
|
|
|
1
|
+
const { Octokit } = require('@octokit/rest');
|
|
2
|
+
const { execSync, spawnSync } = require('child_process');
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const os = require('os');
|
|
6
|
+
const https = require('https');
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Binary analyzer for Redpanda Connect
|
|
10
|
+
* Analyzes OSS, Cloud, and cgo binaries to determine:
|
|
11
|
+
* - Which connectors are available in Cloud (cloud support)
|
|
12
|
+
* - Which connectors require cgo builds (cgo-only)
|
|
13
|
+
* - Which connectors are self-hosted only
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
// Initialize Octokit with optional authentication
|
|
17
|
+
const octokit = new Octokit({
|
|
18
|
+
auth: process.env.GITHUB_TOKEN,
|
|
19
|
+
userAgent: 'redpanda-docs-tools',
|
|
20
|
+
retry: {
|
|
21
|
+
enabled: true,
|
|
22
|
+
retries: 3
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
const REPO_OWNER = 'redpanda-data';
|
|
27
|
+
const REPO_NAME = 'connect';
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Get platform-specific binary name
|
|
31
|
+
* @returns {Object} Platform and arch info
|
|
32
|
+
*/
|
|
33
|
+
function getPlatformInfo() {
|
|
34
|
+
const os = require('os');
|
|
35
|
+
const platform = os.platform();
|
|
36
|
+
const arch = os.arch();
|
|
37
|
+
|
|
38
|
+
// Map Node.js platform names to binary names
|
|
39
|
+
const platformMap = {
|
|
40
|
+
'darwin': 'darwin',
|
|
41
|
+
'linux': 'linux',
|
|
42
|
+
'win32': 'windows'
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
const archMap = {
|
|
46
|
+
'x64': 'amd64',
|
|
47
|
+
'arm64': 'arm64',
|
|
48
|
+
'arm': 'arm64'
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
return {
|
|
52
|
+
platform: platformMap[platform] || 'linux',
|
|
53
|
+
arch: archMap[arch] || 'amd64'
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Get the latest release version from GitHub
|
|
59
|
+
* @param {string} binaryType - Type of binary ('cloud' or 'cgo')
|
|
60
|
+
* @returns {Promise<string>} Version string (e.g., "4.76.0")
|
|
61
|
+
*/
|
|
62
|
+
async function getLatestVersion(binaryType = 'cloud') {
|
|
63
|
+
try {
|
|
64
|
+
const { data: releases } = await octokit.repos.listReleases({
|
|
65
|
+
owner: REPO_OWNER,
|
|
66
|
+
repo: REPO_NAME,
|
|
67
|
+
per_page: 20
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
const prefix = binaryType === 'cloud' ? 'redpanda-connect-cloud' : 'redpanda-connect-cgo';
|
|
71
|
+
|
|
72
|
+
// Find the latest release with the specified binary type
|
|
73
|
+
for (const release of releases) {
|
|
74
|
+
const asset = release.assets.find(a =>
|
|
75
|
+
a.name.startsWith(prefix) &&
|
|
76
|
+
a.name.includes('linux') &&
|
|
77
|
+
a.name.includes('amd64')
|
|
78
|
+
);
|
|
79
|
+
|
|
80
|
+
if (asset) {
|
|
81
|
+
const version = release.tag_name.replace(/^v/, '');
|
|
82
|
+
return version;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
throw new Error(`No ${binaryType} binary found in recent releases`);
|
|
87
|
+
} catch (error) {
|
|
88
|
+
if (error.status === 403 && error.message.includes('rate limit')) {
|
|
89
|
+
throw new Error(
|
|
90
|
+
'GitHub API rate limit exceeded. Set GITHUB_TOKEN environment variable to increase limit from 60 to 5000 requests/hour.'
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
throw new Error(`Failed to fetch latest version: ${error.message}`);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Download a file from a URL
|
|
99
|
+
* @param {string} url - URL to download
|
|
100
|
+
* @param {string} destPath - Destination file path
|
|
101
|
+
* @returns {Promise<void>}
|
|
102
|
+
*/
|
|
103
|
+
function downloadFile(url, destPath) {
|
|
104
|
+
return new Promise((resolve, reject) => {
|
|
105
|
+
https.get(url, { headers: { 'User-Agent': 'redpanda-docs-tools' } }, (res) => {
|
|
106
|
+
// Follow redirects
|
|
107
|
+
if (res.statusCode === 302 || res.statusCode === 301) {
|
|
108
|
+
downloadFile(res.headers.location, destPath).then(resolve).catch(reject);
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
if (res.statusCode !== 200) {
|
|
113
|
+
reject(new Error(`Failed to download: HTTP ${res.statusCode}`));
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const fileStream = fs.createWriteStream(destPath);
|
|
118
|
+
res.pipe(fileStream);
|
|
119
|
+
|
|
120
|
+
fileStream.on('finish', () => {
|
|
121
|
+
fileStream.close();
|
|
122
|
+
resolve();
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
fileStream.on('error', (err) => {
|
|
126
|
+
if (fs.existsSync(destPath)) {
|
|
127
|
+
fs.unlinkSync(destPath);
|
|
128
|
+
}
|
|
129
|
+
reject(err);
|
|
130
|
+
});
|
|
131
|
+
}).on('error', (err) => {
|
|
132
|
+
reject(err);
|
|
133
|
+
});
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Download a binary from GitHub releases
|
|
139
|
+
* @param {string} binaryType - 'cloud' or 'cgo'
|
|
140
|
+
* @param {string} version - Version to download (e.g., "4.76.0")
|
|
141
|
+
* @param {string} destDir - Destination directory
|
|
142
|
+
* @returns {Promise<string>} Path to downloaded binary
|
|
143
|
+
*/
|
|
144
|
+
async function downloadBinary(binaryType, version, destDir) {
|
|
145
|
+
// cgo binaries are only available for linux/amd64, so force that platform
|
|
146
|
+
const platformInfo = binaryType === 'cgo'
|
|
147
|
+
? { platform: 'linux', arch: 'amd64' }
|
|
148
|
+
: getPlatformInfo();
|
|
149
|
+
const prefix = binaryType === 'cloud' ? 'redpanda-connect-cloud' : 'redpanda-connect-cgo';
|
|
150
|
+
const binaryName = `${prefix}-${version}-${platformInfo.platform}-${platformInfo.arch}`;
|
|
151
|
+
const destPath = path.join(destDir, binaryName);
|
|
152
|
+
|
|
153
|
+
// Skip download if already exists
|
|
154
|
+
if (fs.existsSync(destPath)) {
|
|
155
|
+
console.log(`โ ${binaryType.toUpperCase()} binary already downloaded: ${binaryName}`);
|
|
156
|
+
return destPath;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
console.log(`Downloading ${binaryType.toUpperCase()} binary v${version} for ${platformInfo.platform}/${platformInfo.arch}...`);
|
|
160
|
+
|
|
161
|
+
try {
|
|
162
|
+
// Get release information
|
|
163
|
+
const { data: release } = await octokit.repos.getReleaseByTag({
|
|
164
|
+
owner: REPO_OWNER,
|
|
165
|
+
repo: REPO_NAME,
|
|
166
|
+
tag: `v${version}`
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
// Find the asset
|
|
170
|
+
const assetName = `${prefix}_${version}_${platformInfo.platform}_${platformInfo.arch}.tar.gz`;
|
|
171
|
+
const asset = release.assets.find(a => a.name === assetName);
|
|
172
|
+
|
|
173
|
+
if (!asset) {
|
|
174
|
+
throw new Error(`${binaryType.toUpperCase()} binary not found for version ${version} (${assetName})`);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// Download the tarball
|
|
178
|
+
const tarballPath = `${destPath}.tar.gz`;
|
|
179
|
+
await downloadFile(asset.browser_download_url, tarballPath);
|
|
180
|
+
|
|
181
|
+
// Extract the binary
|
|
182
|
+
console.log(`Extracting ${binaryType.toUpperCase()} binary...`);
|
|
183
|
+
execSync(`tar -xzf "${tarballPath}" -C "${destDir}"`, { stdio: 'ignore' });
|
|
184
|
+
|
|
185
|
+
// Find the extracted binary
|
|
186
|
+
const extractedFiles = fs.readdirSync(destDir);
|
|
187
|
+
let binaryPath = null;
|
|
188
|
+
|
|
189
|
+
for (const file of extractedFiles) {
|
|
190
|
+
const fullPath = path.join(destDir, file);
|
|
191
|
+
if (fs.statSync(fullPath).isFile() && file.includes('redpanda-connect') && !file.endsWith('.tar.gz')) {
|
|
192
|
+
binaryPath = fullPath;
|
|
193
|
+
break;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
if (!binaryPath) {
|
|
198
|
+
throw new Error('Binary not found after extraction');
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// Rename to standard name if needed
|
|
202
|
+
if (binaryPath !== destPath) {
|
|
203
|
+
fs.renameSync(binaryPath, destPath);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// Make executable
|
|
207
|
+
fs.chmodSync(destPath, 0o755);
|
|
208
|
+
|
|
209
|
+
// Clean up tarball
|
|
210
|
+
fs.unlinkSync(tarballPath);
|
|
211
|
+
|
|
212
|
+
console.log(`Done: Downloaded ${binaryType.toUpperCase()} binary: ${binaryName}`);
|
|
213
|
+
return destPath;
|
|
214
|
+
} catch (error) {
|
|
215
|
+
if (error.status === 404) {
|
|
216
|
+
throw new Error(`Release v${version} not found or ${binaryType} binary not available for this version`);
|
|
217
|
+
} else if (error.status === 403) {
|
|
218
|
+
throw new Error('GitHub API rate limit exceeded. Set GITHUB_TOKEN environment variable.');
|
|
219
|
+
}
|
|
220
|
+
throw new Error(`Failed to download ${binaryType} binary: ${error.message}`);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* Download cloud binary from GitHub releases
|
|
226
|
+
* @param {string} version - Version to download
|
|
227
|
+
* @param {string} destDir - Destination directory
|
|
228
|
+
* @returns {Promise<string>} Path to downloaded binary
|
|
229
|
+
*/
|
|
230
|
+
async function downloadCloudBinary(version, destDir) {
|
|
231
|
+
return downloadBinary('cloud', version, destDir);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* Download cgo binary from GitHub releases
|
|
236
|
+
* @param {string} version - Version to download
|
|
237
|
+
* @param {string} destDir - Destination directory
|
|
238
|
+
* @returns {Promise<string>} Path to downloaded binary
|
|
239
|
+
*/
|
|
240
|
+
async function downloadCgoBinary(version, destDir) {
|
|
241
|
+
return downloadBinary('cgo', version, destDir);
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* Get list of connectors from a binary
|
|
246
|
+
* @param {string} binaryPath - Path to binary
|
|
247
|
+
* @returns {object} Connector index (same format as rpk connect list --format json-full)
|
|
248
|
+
*/
|
|
249
|
+
function getConnectorList(binaryPath) {
|
|
250
|
+
try {
|
|
251
|
+
const binaryName = path.basename(binaryPath);
|
|
252
|
+
console.log(`Inspecting ${binaryName} for connectors...`);
|
|
253
|
+
|
|
254
|
+
// Check if this is a Linux binary on non-Linux platform (need Docker)
|
|
255
|
+
const isLinuxBinary = binaryName.includes('linux');
|
|
256
|
+
const needsDocker = isLinuxBinary && os.platform() !== 'linux';
|
|
257
|
+
|
|
258
|
+
let result;
|
|
259
|
+
if (needsDocker) {
|
|
260
|
+
// Use Docker to run Linux binaries on macOS/Windows
|
|
261
|
+
const binaryDir = path.dirname(binaryPath);
|
|
262
|
+
const binaryFile = path.basename(binaryPath);
|
|
263
|
+
|
|
264
|
+
// Install dependencies for cgo binaries (libzmq) and run
|
|
265
|
+
const isCgoBinary = binaryFile.includes('cgo');
|
|
266
|
+
const command = isCgoBinary
|
|
267
|
+
? `apt-get update -qq && apt-get install -qq -y libzmq5 > /dev/null 2>&1 && chmod +x ./${binaryFile} && ./${binaryFile} list --format json-full`
|
|
268
|
+
: `chmod +x ./${binaryFile} && ./${binaryFile} list --format json-full`;
|
|
269
|
+
|
|
270
|
+
result = spawnSync('docker', [
|
|
271
|
+
'run', '--rm', '--platform', 'linux/amd64',
|
|
272
|
+
'-v', `${binaryDir}:/work`,
|
|
273
|
+
'-w', '/work',
|
|
274
|
+
'ubuntu:22.04',
|
|
275
|
+
'bash', '-c', command
|
|
276
|
+
], {
|
|
277
|
+
stdio: ['ignore', 'pipe', 'ignore'],
|
|
278
|
+
maxBuffer: 10 * 1024 * 1024 // 10MB buffer
|
|
279
|
+
});
|
|
280
|
+
} else {
|
|
281
|
+
// Run natively
|
|
282
|
+
result = spawnSync(binaryPath, ['list', '--format', 'json-full'], {
|
|
283
|
+
stdio: ['ignore', 'pipe', 'ignore'],
|
|
284
|
+
maxBuffer: 10 * 1024 * 1024 // 10MB buffer
|
|
285
|
+
});
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
if (result.error) {
|
|
289
|
+
throw new Error(`Failed to execute binary: ${result.error.message}`);
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
if (result.status !== 0) {
|
|
293
|
+
throw new Error(`Binary exited with code ${result.status}`);
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
const output = result.stdout.toString();
|
|
297
|
+
const connectors = JSON.parse(output);
|
|
298
|
+
|
|
299
|
+
const typeCount = Object.keys(connectors).filter(k => Array.isArray(connectors[k])).length;
|
|
300
|
+
console.log(`Done: Found ${typeCount} connector types in ${binaryName}`);
|
|
301
|
+
|
|
302
|
+
return connectors;
|
|
303
|
+
} catch (err) {
|
|
304
|
+
throw new Error(`Failed to get connector list: ${err.message}`);
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
/**
|
|
309
|
+
* Deprecated alias for backward compatibility
|
|
310
|
+
* @deprecated Use getConnectorList instead
|
|
311
|
+
*/
|
|
312
|
+
function getCloudConnectors(binaryPath) {
|
|
313
|
+
return getConnectorList(binaryPath);
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
/**
|
|
317
|
+
* Build a set of connector keys for comparison
|
|
318
|
+
* @param {object} index - Connector index
|
|
319
|
+
* @returns {Set<string>} Set of "type:name" keys
|
|
320
|
+
*/
|
|
321
|
+
function buildConnectorSet(index) {
|
|
322
|
+
const connectorSet = new Set();
|
|
323
|
+
|
|
324
|
+
// Iterate through all connector types
|
|
325
|
+
const types = Object.keys(index).filter(key => Array.isArray(index[key]));
|
|
326
|
+
|
|
327
|
+
types.forEach(type => {
|
|
328
|
+
index[type].forEach(connector => {
|
|
329
|
+
if (connector.name) {
|
|
330
|
+
connectorSet.add(`${type}:${connector.name}`);
|
|
331
|
+
}
|
|
332
|
+
});
|
|
333
|
+
});
|
|
334
|
+
|
|
335
|
+
return connectorSet;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
/**
|
|
339
|
+
* Compare OSS connectors with cloud connectors
|
|
340
|
+
* @param {object} ossIndex - OSS connector index
|
|
341
|
+
* @param {object} cloudIndex - Cloud connector index
|
|
342
|
+
* @returns {object} Comparison results
|
|
343
|
+
*/
|
|
344
|
+
function compareOssWithCloud(ossIndex, cloudIndex) {
|
|
345
|
+
const ossSet = buildConnectorSet(ossIndex);
|
|
346
|
+
const cloudSet = buildConnectorSet(cloudIndex);
|
|
347
|
+
|
|
348
|
+
const inCloud = [];
|
|
349
|
+
const notInCloud = [];
|
|
350
|
+
const cloudOnly = [];
|
|
351
|
+
|
|
352
|
+
// Find OSS connectors and check if they're in cloud
|
|
353
|
+
ossSet.forEach(key => {
|
|
354
|
+
const [type, name] = key.split(':');
|
|
355
|
+
const ossConnector = ossIndex[type]?.find(c => c.name === name);
|
|
356
|
+
|
|
357
|
+
if (cloudSet.has(key)) {
|
|
358
|
+
inCloud.push({
|
|
359
|
+
type,
|
|
360
|
+
name,
|
|
361
|
+
status: ossConnector?.status || ossConnector?.type || 'stable'
|
|
362
|
+
});
|
|
363
|
+
} else {
|
|
364
|
+
notInCloud.push({
|
|
365
|
+
type,
|
|
366
|
+
name,
|
|
367
|
+
status: ossConnector?.status || ossConnector?.type || 'stable',
|
|
368
|
+
reason: 'self-hosted-only'
|
|
369
|
+
});
|
|
370
|
+
}
|
|
371
|
+
});
|
|
372
|
+
|
|
373
|
+
// Find cloud-only connectors (in cloud but NOT in OSS)
|
|
374
|
+
cloudSet.forEach(key => {
|
|
375
|
+
if (!ossSet.has(key)) {
|
|
376
|
+
const [type, name] = key.split(':');
|
|
377
|
+
const cloudConnector = cloudIndex[type]?.find(c => c.name === name);
|
|
378
|
+
|
|
379
|
+
if (cloudConnector) {
|
|
380
|
+
cloudOnly.push({
|
|
381
|
+
...cloudConnector, // Include full connector schema
|
|
382
|
+
type,
|
|
383
|
+
name,
|
|
384
|
+
status: cloudConnector.status || 'stable',
|
|
385
|
+
reason: 'cloud-only'
|
|
386
|
+
});
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
});
|
|
390
|
+
|
|
391
|
+
return {
|
|
392
|
+
inCloud,
|
|
393
|
+
notInCloud,
|
|
394
|
+
cloudOnly,
|
|
395
|
+
totalOss: ossSet.size,
|
|
396
|
+
totalCloud: cloudSet.size
|
|
397
|
+
};
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
/**
|
|
401
|
+
* Find connectors that are only available in cgo builds
|
|
402
|
+
* @param {object} ossIndex - OSS connector index (from rpk connect)
|
|
403
|
+
* @param {object} cgoIndex - cgo connector index
|
|
404
|
+
* @returns {Array<object>} List of cgo-only connectors
|
|
405
|
+
*/
|
|
406
|
+
function findCgoOnlyConnectors(ossIndex, cgoIndex) {
|
|
407
|
+
const ossSet = buildConnectorSet(ossIndex);
|
|
408
|
+
const cgoSet = buildConnectorSet(cgoIndex);
|
|
409
|
+
|
|
410
|
+
const cgoOnly = [];
|
|
411
|
+
|
|
412
|
+
cgoSet.forEach(key => {
|
|
413
|
+
if (!ossSet.has(key)) {
|
|
414
|
+
const [type, name] = key.split(':');
|
|
415
|
+
const connector = cgoIndex[type]?.find(c => c.name === name);
|
|
416
|
+
if (connector) {
|
|
417
|
+
// Return the full connector object with all fields and examples
|
|
418
|
+
cgoOnly.push({
|
|
419
|
+
...connector,
|
|
420
|
+
type, // Add type field for convenience
|
|
421
|
+
requiresCgo: true // Mark as cgo-required
|
|
422
|
+
});
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
});
|
|
426
|
+
|
|
427
|
+
return cgoOnly;
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
/**
|
|
431
|
+
* Analyze all binary types for connector support
|
|
432
|
+
* @param {string} ossVersion - OSS version to check
|
|
433
|
+
* @param {string} [cloudVersion] - Cloud version (auto-detected if not provided)
|
|
434
|
+
* @param {string} [dataDir] - Directory for docs-data (analysis results only)
|
|
435
|
+
* @param {object} [options] - Analysis options
|
|
436
|
+
* @param {boolean} [options.skipCloud] - Skip cloud binary analysis
|
|
437
|
+
* @param {boolean} [options.skipCgo] - Skip cgo binary analysis
|
|
438
|
+
* @param {string} [options.cgoVersion] - cgo version (defaults to cloudVersion)
|
|
439
|
+
* @returns {Promise<object>} Analysis results
|
|
440
|
+
*/
|
|
441
|
+
async function analyzeAllBinaries(ossVersion, cloudVersion = null, dataDir = null, options = {}) {
|
|
442
|
+
const { skipCloud = false, skipCgo = false, cgoVersion = null } = options;
|
|
443
|
+
|
|
444
|
+
const docsDataDir = dataDir || path.resolve(process.cwd(), 'docs-data');
|
|
445
|
+
|
|
446
|
+
// Use temp directory for binaries (not docs-data)
|
|
447
|
+
const os = require('os');
|
|
448
|
+
const binaryDir = fs.mkdtempSync(path.join(os.tmpdir(), 'rpcn-binaries-'));
|
|
449
|
+
|
|
450
|
+
console.log(`๐ Using temp directory for binaries: ${binaryDir}`);
|
|
451
|
+
|
|
452
|
+
// Load OSS connector data (from rpk connect list)
|
|
453
|
+
const ossDataPath = path.join(docsDataDir, `connect-${ossVersion}.json`);
|
|
454
|
+
if (!fs.existsSync(ossDataPath)) {
|
|
455
|
+
throw new Error(`OSS data file not found: ${ossDataPath}. Run 'rpk connect list --format json-full > ${ossDataPath}' first.`);
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
const ossIndex = JSON.parse(fs.readFileSync(ossDataPath, 'utf8'));
|
|
459
|
+
|
|
460
|
+
// Cloud binary analysis
|
|
461
|
+
let cloudIndex = null;
|
|
462
|
+
let comparison = null;
|
|
463
|
+
|
|
464
|
+
if (!skipCloud) {
|
|
465
|
+
// Auto-detect cloud version if not provided
|
|
466
|
+
if (!cloudVersion) {
|
|
467
|
+
console.log('Detecting latest cloud version...');
|
|
468
|
+
cloudVersion = await getLatestVersion('cloud');
|
|
469
|
+
console.log(`โ Latest cloud version: ${cloudVersion}`);
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
// Download and inspect cloud binary
|
|
473
|
+
const cloudBinaryPath = await downloadCloudBinary(cloudVersion, binaryDir);
|
|
474
|
+
cloudIndex = getConnectorList(cloudBinaryPath);
|
|
475
|
+
|
|
476
|
+
// Compare OSS with Cloud
|
|
477
|
+
comparison = compareOssWithCloud(ossIndex, cloudIndex);
|
|
478
|
+
} else {
|
|
479
|
+
console.log('โฉ Skipping cloud binary analysis');
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
// cgo binary analysis
|
|
483
|
+
let cgoOnly = [];
|
|
484
|
+
let cgoIndex = null;
|
|
485
|
+
|
|
486
|
+
if (!skipCgo) {
|
|
487
|
+
console.log('\nAnalyzing cgo binary...');
|
|
488
|
+
try {
|
|
489
|
+
// Use cgo-specific version or fall back to cloud version
|
|
490
|
+
let effectiveCgoVersion = cgoVersion || cloudVersion;
|
|
491
|
+
|
|
492
|
+
// If neither is provided, auto-detect
|
|
493
|
+
if (!effectiveCgoVersion) {
|
|
494
|
+
console.log('Detecting latest cgo version...');
|
|
495
|
+
effectiveCgoVersion = await getLatestVersion('cgo');
|
|
496
|
+
console.log(`โ Latest cgo version: ${effectiveCgoVersion}`);
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
const cgoBinaryPath = await downloadCgoBinary(effectiveCgoVersion, binaryDir);
|
|
500
|
+
cgoIndex = getConnectorList(cgoBinaryPath);
|
|
501
|
+
|
|
502
|
+
// Find connectors only in cgo (not in regular OSS binary)
|
|
503
|
+
// These are connectors that require cgo-enabled builds
|
|
504
|
+
cgoOnly = findCgoOnlyConnectors(ossIndex, cgoIndex);
|
|
505
|
+
|
|
506
|
+
console.log(`Done: Cgo analysis complete: ${cgoOnly.length} cgo-only connector(s) found`);
|
|
507
|
+
if (cgoOnly.length > 0) {
|
|
508
|
+
console.log(' cgo-only connectors:');
|
|
509
|
+
cgoOnly.forEach(c => console.log(` โข ${c.type}/${c.name} (${c.status})`));
|
|
510
|
+
}
|
|
511
|
+
} catch (err) {
|
|
512
|
+
console.error(`Warning: Cgo analysis failed: ${err.message}`);
|
|
513
|
+
console.error(' Continuing without cgo data...');
|
|
514
|
+
}
|
|
515
|
+
} else {
|
|
516
|
+
console.log('โฉ Skipping cgo binary analysis');
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
// Clean up temp binaries
|
|
520
|
+
try {
|
|
521
|
+
fs.rmSync(binaryDir, { recursive: true, force: true });
|
|
522
|
+
console.log(`๐งน Cleaned up temp binaries`);
|
|
523
|
+
} catch (err) {
|
|
524
|
+
console.warn(`Warning: Could not clean up temp directory: ${err.message}`);
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
// IMPORTANT: cgoOnly contains connectors that exist ONLY in the cgo binary
|
|
528
|
+
// and NOT in the regular OSS binary (rpk connect list). These are the
|
|
529
|
+
// connectors that require cgo-enabled builds. Connectors available in the
|
|
530
|
+
// standard OSS binary do NOT require cgo and will NOT be in this list.
|
|
531
|
+
//
|
|
532
|
+
// As of v4.76.0, cgo-only connectors are:
|
|
533
|
+
// - inputs/tigerbeetle_cdc (beta)
|
|
534
|
+
// - processors/ffi (experimental)
|
|
535
|
+
return {
|
|
536
|
+
ossVersion,
|
|
537
|
+
cloudVersion,
|
|
538
|
+
cgoVersion: cgoVersion || cloudVersion, // The version analyzed for cgo
|
|
539
|
+
comparison,
|
|
540
|
+
cloudIndex,
|
|
541
|
+
cgoIndex,
|
|
542
|
+
cgoOnly
|
|
543
|
+
};
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
/**
|
|
547
|
+
* Deprecated alias for backward compatibility
|
|
548
|
+
* @deprecated Use analyzeAllBinaries instead
|
|
549
|
+
*/
|
|
550
|
+
async function getCloudSupport(ossVersion, cloudVersion = null, dataDir = null) {
|
|
551
|
+
return analyzeAllBinaries(ossVersion, cloudVersion, dataDir);
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
/**
|
|
555
|
+
* Check GitHub API rate limit
|
|
556
|
+
* @returns {Promise<object>} Rate limit information
|
|
557
|
+
*/
|
|
558
|
+
async function checkRateLimit() {
|
|
559
|
+
try {
|
|
560
|
+
const { data } = await octokit.rateLimit.get();
|
|
561
|
+
return {
|
|
562
|
+
remaining: data.rate.remaining,
|
|
563
|
+
limit: data.rate.limit,
|
|
564
|
+
reset: new Date(data.rate.reset * 1000),
|
|
565
|
+
used: data.rate.used
|
|
566
|
+
};
|
|
567
|
+
} catch (error) {
|
|
568
|
+
throw new Error(`Failed to check rate limit: ${error.message}`);
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
module.exports = {
|
|
573
|
+
// Primary functions
|
|
574
|
+
analyzeAllBinaries,
|
|
575
|
+
findCgoOnlyConnectors,
|
|
576
|
+
getLatestVersion,
|
|
577
|
+
downloadCloudBinary,
|
|
578
|
+
downloadCgoBinary,
|
|
579
|
+
getConnectorList,
|
|
580
|
+
buildConnectorSet,
|
|
581
|
+
compareOssWithCloud,
|
|
582
|
+
checkRateLimit,
|
|
583
|
+
|
|
584
|
+
// Deprecated aliases (for backward compatibility)
|
|
585
|
+
getCloudSupport,
|
|
586
|
+
getLatestCloudVersion: () => getLatestVersion('cloud'),
|
|
587
|
+
getCloudConnectors: getConnectorList
|
|
588
|
+
};
|