@redpanda-data/docs-extensions-and-macros 4.15.5 → 4.15.7
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 +2 -0
- package/extensions/convert-to-markdown.js +14 -4
- package/package.json +1 -1
- package/tools/redpanda-connect/AUTOMATION.md +834 -0
- package/tools/redpanda-connect/generate-rpcn-connector-docs.js +20 -1
- package/tools/redpanda-connect/github-release-utils.js +275 -0
- package/tools/redpanda-connect/helpers/buildConfigYaml.js +13 -8
- package/tools/redpanda-connect/multi-version-summary.js +92 -0
- package/tools/redpanda-connect/parse-csv-connectors.js +1 -0
- package/tools/redpanda-connect/pr-summary-formatter.js +381 -12
- package/tools/redpanda-connect/rpcn-connector-docs-handler.js +462 -66
- package/tools/redpanda-connect/templates/connector.hbs +1 -0
|
@@ -244,7 +244,8 @@ async function generateRpcnConnectorDocs(options) {
|
|
|
244
244
|
templateBloblang,
|
|
245
245
|
writeFullDrafts,
|
|
246
246
|
cgoOnly = [], // Array of cgo-only connectors from cgo binary inspection
|
|
247
|
-
cloudOnly = []
|
|
247
|
+
cloudOnly = [], // Array of cloud-only connectors from cloud binary inspection
|
|
248
|
+
csvMetadata = [] // Array of CSV metadata with support levels
|
|
248
249
|
} = options;
|
|
249
250
|
|
|
250
251
|
// Read connector index (JSON or YAML)
|
|
@@ -274,6 +275,16 @@ async function generateRpcnConnectorDocs(options) {
|
|
|
274
275
|
});
|
|
275
276
|
}
|
|
276
277
|
|
|
278
|
+
// Build a Map of CSV metadata for fast support level lookup
|
|
279
|
+
const csvMetadataMap = new Map();
|
|
280
|
+
if (Array.isArray(csvMetadata)) {
|
|
281
|
+
csvMetadata.forEach(item => {
|
|
282
|
+
if (item.type && item.name) {
|
|
283
|
+
csvMetadataMap.set(`${item.type}:${item.name}`, item);
|
|
284
|
+
}
|
|
285
|
+
});
|
|
286
|
+
}
|
|
287
|
+
|
|
277
288
|
// Apply overrides if provided
|
|
278
289
|
if (overrides && fs.existsSync(overrides)) {
|
|
279
290
|
const ovRaw = fs.readFileSync(overrides, 'utf8');
|
|
@@ -395,6 +406,7 @@ async function generateRpcnConnectorDocs(options) {
|
|
|
395
406
|
}
|
|
396
407
|
|
|
397
408
|
// Check if this connector is cgo-only or cloud-only and mark it
|
|
409
|
+
// Use plural type for CGO/cloud detection (matches test expectations)
|
|
398
410
|
const connectorKey = `${type}:${name}`;
|
|
399
411
|
const isCloudOnly = cloudOnlySet.has(connectorKey);
|
|
400
412
|
const isCgoOnly = cgoOnlySet.has(connectorKey);
|
|
@@ -411,6 +423,13 @@ async function generateRpcnConnectorDocs(options) {
|
|
|
411
423
|
item.cloudOnly = true;
|
|
412
424
|
}
|
|
413
425
|
|
|
426
|
+
// Lookup support level from CSV metadata using singular type
|
|
427
|
+
const csvKey = `${item.type}:${name}`;
|
|
428
|
+
const csvData = csvMetadataMap.get(csvKey);
|
|
429
|
+
if (csvData && csvData.support) {
|
|
430
|
+
item.support = csvData.support;
|
|
431
|
+
}
|
|
432
|
+
|
|
414
433
|
let content;
|
|
415
434
|
try {
|
|
416
435
|
content = compiledTemplate(item);
|
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const octokit = require('../../cli-utils/octokit-client');
|
|
4
|
+
const { hasGitHubToken } = require('../../cli-utils/github-token');
|
|
5
|
+
const semver = require('semver');
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* GitHub release discovery utilities for Redpanda Connect
|
|
9
|
+
*
|
|
10
|
+
* Provides functions to discover and filter GitHub releases between versions.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
// Cache for GitHub releases to avoid repeated API calls
|
|
14
|
+
let releaseCache = null;
|
|
15
|
+
let cacheTimestamp = null;
|
|
16
|
+
const CACHE_TTL_MS = 5 * 60 * 1000; // 5 minutes
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Fetch all releases from redpanda-data/connect repository
|
|
20
|
+
* @param {boolean} useCache - Whether to use cached results
|
|
21
|
+
* @returns {Promise<Array>} Array of release objects
|
|
22
|
+
*/
|
|
23
|
+
async function fetchAllReleases(useCache = true) {
|
|
24
|
+
// Check cache
|
|
25
|
+
if (useCache && releaseCache && cacheTimestamp) {
|
|
26
|
+
const age = Date.now() - cacheTimestamp;
|
|
27
|
+
if (age < CACHE_TTL_MS) {
|
|
28
|
+
console.log(`✓ Using cached releases (${Math.round(age / 1000)}s old)`);
|
|
29
|
+
return releaseCache;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
console.log('Fetching releases from GitHub...');
|
|
34
|
+
|
|
35
|
+
// Warn if no token available (only once per execution)
|
|
36
|
+
if (!hasGitHubToken() && !fetchAllReleases._warnedAboutToken) {
|
|
37
|
+
console.warn('⚠️ No GitHub token found. API rate limits will be more restrictive.');
|
|
38
|
+
console.warn(' Set GITHUB_TOKEN or GH_TOKEN environment variable for higher limits.');
|
|
39
|
+
fetchAllReleases._warnedAboutToken = true;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
try {
|
|
43
|
+
// Fetch all releases (paginated)
|
|
44
|
+
const releases = await octokit.paginate(
|
|
45
|
+
octokit.rest.repos.listReleases,
|
|
46
|
+
{
|
|
47
|
+
owner: 'redpanda-data',
|
|
48
|
+
repo: 'connect',
|
|
49
|
+
per_page: 100
|
|
50
|
+
}
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
console.log(`✓ Fetched ${releases.length} releases from GitHub`);
|
|
54
|
+
|
|
55
|
+
// Update cache
|
|
56
|
+
releaseCache = releases;
|
|
57
|
+
cacheTimestamp = Date.now();
|
|
58
|
+
|
|
59
|
+
return releases;
|
|
60
|
+
} catch (error) {
|
|
61
|
+
if (error.status === 403 && error.response?.headers?.['x-ratelimit-remaining'] === '0') {
|
|
62
|
+
const resetTime = new Date(parseInt(error.response.headers['x-ratelimit-reset']) * 1000);
|
|
63
|
+
throw new Error(
|
|
64
|
+
`GitHub API rate limit exceeded. Resets at ${resetTime.toLocaleTimeString()}. ` +
|
|
65
|
+
`Consider setting GITHUB_TOKEN environment variable for higher limits.`
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
throw new Error(`Failed to fetch GitHub releases: ${error.message}`);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Parse version from GitHub release tag
|
|
75
|
+
* Handles formats: "v4.50.0", "4.50.0", "v4.50.0-beta.1"
|
|
76
|
+
* @param {string} tag - Release tag name
|
|
77
|
+
* @returns {string|null} Normalized version string or null if invalid
|
|
78
|
+
*/
|
|
79
|
+
function parseVersionFromTag(tag) {
|
|
80
|
+
if (!tag) return null;
|
|
81
|
+
|
|
82
|
+
// Remove 'v' prefix if present
|
|
83
|
+
const normalized = tag.startsWith('v') ? tag.slice(1) : tag;
|
|
84
|
+
|
|
85
|
+
// Validate semver format
|
|
86
|
+
const version = semver.valid(normalized);
|
|
87
|
+
return version;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Check if a version is a pre-release (beta, RC, alpha, etc.)
|
|
92
|
+
* @param {string} version - Semver version string
|
|
93
|
+
* @returns {boolean} True if pre-release
|
|
94
|
+
*/
|
|
95
|
+
function isPrerelease(version) {
|
|
96
|
+
const parsed = semver.parse(version);
|
|
97
|
+
if (!parsed) return false;
|
|
98
|
+
|
|
99
|
+
return parsed.prerelease.length > 0;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Filter releases to stable GA releases only
|
|
104
|
+
* @param {Array} releases - Array of GitHub release objects
|
|
105
|
+
* @returns {Array} Filtered releases with only stable versions
|
|
106
|
+
*/
|
|
107
|
+
function filterToStableReleases(releases) {
|
|
108
|
+
return releases.filter(release => {
|
|
109
|
+
// Skip drafts
|
|
110
|
+
if (release.draft) {
|
|
111
|
+
return false;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// Parse version
|
|
115
|
+
const version = parseVersionFromTag(release.tag_name);
|
|
116
|
+
if (!version) {
|
|
117
|
+
return false;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Skip pre-releases (beta, RC, etc.)
|
|
121
|
+
if (isPrerelease(version)) {
|
|
122
|
+
return false;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
return true;
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Discover all intermediate releases between two versions
|
|
131
|
+
* @param {string} fromVersion - Starting version (e.g., "4.50.0")
|
|
132
|
+
* @param {string} toVersion - Ending version (e.g., "4.54.0")
|
|
133
|
+
* @param {Object} options - Optional configuration
|
|
134
|
+
* @param {boolean} options.includePrerelease - Include beta/RC versions (default: false)
|
|
135
|
+
* @param {boolean} options.useCache - Use cached GitHub data (default: true)
|
|
136
|
+
* @returns {Promise<Array>} Array of version objects: [{version, tag, date, url}]
|
|
137
|
+
*/
|
|
138
|
+
async function discoverIntermediateReleases(fromVersion, toVersion, options = {}) {
|
|
139
|
+
const {
|
|
140
|
+
includePrerelease = false,
|
|
141
|
+
useCache = true
|
|
142
|
+
} = options;
|
|
143
|
+
|
|
144
|
+
// Validate versions are strings
|
|
145
|
+
if (typeof fromVersion !== 'string') {
|
|
146
|
+
throw new Error(`Invalid starting version: ${fromVersion}`);
|
|
147
|
+
}
|
|
148
|
+
if (typeof toVersion !== 'string') {
|
|
149
|
+
throw new Error(`Invalid ending version: ${toVersion}`);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// Normalize versions (remove 'v' prefix if present)
|
|
153
|
+
const normalizedFrom = fromVersion.startsWith('v') ? fromVersion.slice(1) : fromVersion;
|
|
154
|
+
const normalizedTo = toVersion.startsWith('v') ? toVersion.slice(1) : toVersion;
|
|
155
|
+
|
|
156
|
+
// Validate versions
|
|
157
|
+
if (!semver.valid(normalizedFrom)) {
|
|
158
|
+
throw new Error(`Invalid starting version: ${fromVersion}`);
|
|
159
|
+
}
|
|
160
|
+
if (!semver.valid(normalizedTo)) {
|
|
161
|
+
throw new Error(`Invalid ending version: ${toVersion}`);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
console.log('');
|
|
165
|
+
console.log(`Discovering releases between ${normalizedFrom} and ${normalizedTo}...`);
|
|
166
|
+
|
|
167
|
+
// Fetch all releases
|
|
168
|
+
const allReleases = await fetchAllReleases(useCache);
|
|
169
|
+
|
|
170
|
+
// Filter to stable releases unless includePrerelease is true
|
|
171
|
+
const filteredReleases = includePrerelease
|
|
172
|
+
? allReleases.filter(r => !r.draft && parseVersionFromTag(r.tag_name))
|
|
173
|
+
: filterToStableReleases(allReleases);
|
|
174
|
+
|
|
175
|
+
// Parse and filter versions in range
|
|
176
|
+
const versionsInRange = [];
|
|
177
|
+
|
|
178
|
+
for (const release of filteredReleases) {
|
|
179
|
+
const version = parseVersionFromTag(release.tag_name);
|
|
180
|
+
if (!version) continue;
|
|
181
|
+
|
|
182
|
+
// Check if version is in range (inclusive)
|
|
183
|
+
if (
|
|
184
|
+
semver.gte(version, normalizedFrom) &&
|
|
185
|
+
semver.lte(version, normalizedTo)
|
|
186
|
+
) {
|
|
187
|
+
versionsInRange.push({
|
|
188
|
+
version,
|
|
189
|
+
tag: release.tag_name,
|
|
190
|
+
date: release.published_at,
|
|
191
|
+
url: release.html_url,
|
|
192
|
+
isPrerelease: isPrerelease(version)
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// Sort by semver (oldest to newest)
|
|
198
|
+
versionsInRange.sort((a, b) => semver.compare(a.version, b.version));
|
|
199
|
+
|
|
200
|
+
console.log(`✓ Found ${versionsInRange.length} release(s) in range`);
|
|
201
|
+
|
|
202
|
+
if (versionsInRange.length === 0) {
|
|
203
|
+
console.warn('⚠️ No releases found in the specified range');
|
|
204
|
+
return [];
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// Log the discovered versions
|
|
208
|
+
console.log('');
|
|
209
|
+
console.log('Releases to process:');
|
|
210
|
+
versionsInRange.forEach((v, i) => {
|
|
211
|
+
const prereleaseTag = v.isPrerelease ? ' (pre-release)' : '';
|
|
212
|
+
const date = new Date(v.date).toLocaleDateString();
|
|
213
|
+
console.log(` ${i + 1}. ${v.version}${prereleaseTag} - ${date}`);
|
|
214
|
+
});
|
|
215
|
+
console.log('');
|
|
216
|
+
|
|
217
|
+
return versionsInRange;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Find the appropriate cloud version for a given OSS release date
|
|
222
|
+
* Returns the latest stable cloud version published on or before the OSS release date
|
|
223
|
+
* @param {string} ossReleaseDate - ISO date string of the OSS release
|
|
224
|
+
* @param {Object} options - Optional configuration
|
|
225
|
+
* @param {boolean} options.useCache - Use cached GitHub data (default: true)
|
|
226
|
+
* @returns {Promise<string|null>} Cloud version string or null if not found
|
|
227
|
+
*/
|
|
228
|
+
async function findCloudVersionForDate(ossReleaseDate, options = {}) {
|
|
229
|
+
const { useCache = true } = options;
|
|
230
|
+
|
|
231
|
+
// Fetch all releases from the main connect repo (includes cloud builds)
|
|
232
|
+
const allReleases = await fetchAllReleases(useCache);
|
|
233
|
+
|
|
234
|
+
// Filter to stable releases
|
|
235
|
+
const stableReleases = filterToStableReleases(allReleases);
|
|
236
|
+
|
|
237
|
+
// Filter to releases on or before the OSS release date
|
|
238
|
+
const ossDate = new Date(ossReleaseDate);
|
|
239
|
+
const eligibleReleases = stableReleases.filter(release => {
|
|
240
|
+
const releaseDate = new Date(release.published_at);
|
|
241
|
+
return releaseDate <= ossDate;
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
if (eligibleReleases.length === 0) {
|
|
245
|
+
return null;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
// Sort by date (most recent first)
|
|
249
|
+
eligibleReleases.sort((a, b) => {
|
|
250
|
+
return new Date(b.published_at) - new Date(a.published_at);
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
// Return the most recent version
|
|
254
|
+
const cloudVersion = parseVersionFromTag(eligibleReleases[0].tag_name);
|
|
255
|
+
return cloudVersion;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* Clear the release cache
|
|
260
|
+
* Useful for testing or when you need fresh data
|
|
261
|
+
*/
|
|
262
|
+
function clearCache() {
|
|
263
|
+
releaseCache = null;
|
|
264
|
+
cacheTimestamp = null;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
module.exports = {
|
|
268
|
+
discoverIntermediateReleases,
|
|
269
|
+
fetchAllReleases,
|
|
270
|
+
parseVersionFromTag,
|
|
271
|
+
isPrerelease,
|
|
272
|
+
filterToStableReleases,
|
|
273
|
+
findCloudVersionForDate,
|
|
274
|
+
clearCache
|
|
275
|
+
};
|
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
const renderLeafField = require('./renderLeafField');
|
|
2
2
|
const renderObjectField = require('./renderObjectField');
|
|
3
3
|
|
|
4
|
+
// Component types that support the 'label' field
|
|
5
|
+
const TYPES_WITH_LABEL = new Set(['inputs', 'outputs', 'processors']);
|
|
6
|
+
|
|
4
7
|
/**
|
|
5
|
-
* Builds either
|
|
8
|
+
* Builds either "Common" or "Advanced" YAML for one connector.
|
|
6
9
|
*
|
|
7
|
-
* - type =
|
|
8
|
-
* - connectorName = such as
|
|
10
|
+
* - type = "input" or "output" (or whatever type)
|
|
11
|
+
* - connectorName = such as "amqp_1"
|
|
9
12
|
* - children = the array of field‐definitions (entry.config.children)
|
|
10
13
|
* - includeAdvanced = if false → only fields where is_advanced !== true
|
|
11
14
|
* if true → all fields (except deprecated)
|
|
@@ -13,18 +16,20 @@ const renderObjectField = require('./renderObjectField');
|
|
|
13
16
|
* Structure produced:
|
|
14
17
|
*
|
|
15
18
|
* type:
|
|
16
|
-
* label: ""
|
|
19
|
+
* label: "" (only for inputs, outputs, processors)
|
|
17
20
|
* connectorName:
|
|
18
|
-
* ...child fields (with comments for
|
|
21
|
+
* ...child fields (with comments for "no default")
|
|
19
22
|
*/
|
|
20
23
|
module.exports = function buildConfigYaml(type, connectorName, children, includeAdvanced) {
|
|
21
24
|
const lines = [];
|
|
22
25
|
|
|
23
|
-
//
|
|
26
|
+
// "type:" top‐level
|
|
24
27
|
lines.push(`${type}:`);
|
|
25
28
|
|
|
26
|
-
// Two‐space indent for
|
|
27
|
-
|
|
29
|
+
// Two‐space indent for "label" (only for types that support it)
|
|
30
|
+
if (TYPES_WITH_LABEL.has(type)) {
|
|
31
|
+
lines.push(` label: ""`);
|
|
32
|
+
}
|
|
28
33
|
|
|
29
34
|
// Two‐space indent for connectorName heading
|
|
30
35
|
lines.push(` ${connectorName}:`);
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Create a master diff JSON that aggregates changes across multiple releases
|
|
8
|
+
*
|
|
9
|
+
* @param {Array} intermediateResults - Array of {fromVersion, toVersion, diffPath, success}
|
|
10
|
+
* @param {string} finalDiffPath - Path to the final diff JSON
|
|
11
|
+
* @param {string} outputPath - Where to write the master diff
|
|
12
|
+
* @returns {Object} Master diff object
|
|
13
|
+
*/
|
|
14
|
+
function createMasterDiff(intermediateResults, finalDiffPath, outputPath) {
|
|
15
|
+
const releases = [];
|
|
16
|
+
|
|
17
|
+
// Add all intermediate releases
|
|
18
|
+
for (const result of intermediateResults) {
|
|
19
|
+
if (!result.success) continue;
|
|
20
|
+
|
|
21
|
+
try {
|
|
22
|
+
const diffData = JSON.parse(fs.readFileSync(result.diffPath, 'utf8'));
|
|
23
|
+
releases.push({
|
|
24
|
+
fromVersion: result.fromVersion,
|
|
25
|
+
toVersion: result.toVersion,
|
|
26
|
+
date: diffData.comparison?.timestamp || new Date().toISOString(),
|
|
27
|
+
summary: diffData.summary || {},
|
|
28
|
+
details: diffData.details || {},
|
|
29
|
+
binaryAnalysis: diffData.binaryAnalysis || null
|
|
30
|
+
});
|
|
31
|
+
} catch (err) {
|
|
32
|
+
console.warn(`Warning: Failed to load ${result.diffPath}: ${err.message}`);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Add the final release
|
|
37
|
+
if (finalDiffPath && fs.existsSync(finalDiffPath)) {
|
|
38
|
+
try {
|
|
39
|
+
const diffData = JSON.parse(fs.readFileSync(finalDiffPath, 'utf8'));
|
|
40
|
+
const finalFromVersion = diffData.comparison?.oldVersion;
|
|
41
|
+
const finalToVersion = diffData.comparison?.newVersion;
|
|
42
|
+
|
|
43
|
+
releases.push({
|
|
44
|
+
fromVersion: finalFromVersion,
|
|
45
|
+
toVersion: finalToVersion,
|
|
46
|
+
date: diffData.comparison?.timestamp || new Date().toISOString(),
|
|
47
|
+
summary: diffData.summary || {},
|
|
48
|
+
details: diffData.details || {},
|
|
49
|
+
binaryAnalysis: diffData.binaryAnalysis || null
|
|
50
|
+
});
|
|
51
|
+
} catch (err) {
|
|
52
|
+
console.warn(`Warning: Failed to load ${finalDiffPath}: ${err.message}`);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Calculate total summary across all releases
|
|
57
|
+
const totalSummary = {
|
|
58
|
+
versions: releases.map(r => r.toVersion),
|
|
59
|
+
releaseCount: releases.length,
|
|
60
|
+
newComponents: releases.reduce((sum, r) => sum + (r.summary.newComponents || 0), 0),
|
|
61
|
+
newFields: releases.reduce((sum, r) => sum + (r.summary.newFields || 0), 0),
|
|
62
|
+
removedComponents: releases.reduce((sum, r) => sum + (r.summary.removedComponents || 0), 0),
|
|
63
|
+
removedFields: releases.reduce((sum, r) => sum + (r.summary.removedFields || 0), 0),
|
|
64
|
+
deprecatedComponents: releases.reduce((sum, r) => sum + (r.summary.deprecatedComponents || 0), 0),
|
|
65
|
+
deprecatedFields: releases.reduce((sum, r) => sum + (r.summary.deprecatedFields || 0), 0),
|
|
66
|
+
changedDefaults: releases.reduce((sum, r) => sum + (r.summary.changedDefaults || 0), 0)
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
const masterDiff = {
|
|
70
|
+
metadata: {
|
|
71
|
+
generatedAt: new Date().toISOString(),
|
|
72
|
+
startVersion: releases[0]?.fromVersion,
|
|
73
|
+
endVersion: releases[releases.length - 1]?.toVersion,
|
|
74
|
+
processedReleases: releases.length
|
|
75
|
+
},
|
|
76
|
+
totalSummary,
|
|
77
|
+
releases
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
// Write to file
|
|
81
|
+
if (outputPath) {
|
|
82
|
+
fs.writeFileSync(outputPath, JSON.stringify(masterDiff, null, 2), 'utf8');
|
|
83
|
+
console.log(`✓ Created master diff: ${path.basename(outputPath)}`);
|
|
84
|
+
console.log(` Spans ${releases.length} release(s): ${releases[0]?.fromVersion} → ${releases[releases.length - 1]?.toVersion}`);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return masterDiff;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
module.exports = {
|
|
91
|
+
createMasterDiff
|
|
92
|
+
};
|
|
@@ -48,6 +48,7 @@ async function parseCSVConnectors(localCsvPath, logger) {
|
|
|
48
48
|
return {
|
|
49
49
|
name: trimmed.name,
|
|
50
50
|
type: trimmed.type,
|
|
51
|
+
support: trimmed.support || 'community', // certified, community, enterprise
|
|
51
52
|
is_cloud_supported: (trimmed.cloud || '').toLowerCase() === 'y' ? 'y' : 'n'
|
|
52
53
|
};
|
|
53
54
|
})
|