@redpanda-data/docs-extensions-and-macros 5.13.4 → 5.13.5
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/macros/enterprise.js
CHANGED
|
@@ -31,6 +31,12 @@
|
|
|
31
31
|
* registry is available (for example, outside an Antora build), the macros
|
|
32
32
|
* fall back to unvalidated rendering with a single warning.
|
|
33
33
|
*
|
|
34
|
+
* A registry entry's `since` field, when set, is checked against the page
|
|
35
|
+
* being converted, not the newest release: a mention or table row for a
|
|
36
|
+
* feature whose `since` postdates the page's own version renders unstyled,
|
|
37
|
+
* the same way an unreleased feature renders on released docs, because as
|
|
38
|
+
* of that page's version the feature had not shipped yet.
|
|
39
|
+
*
|
|
34
40
|
* Document or site attributes:
|
|
35
41
|
*
|
|
36
42
|
* enterprise-validate 'warn' (default) to log unknown feature names,
|
|
@@ -55,6 +61,7 @@
|
|
|
55
61
|
|
|
56
62
|
const yaml = require('js-yaml')
|
|
57
63
|
const chalk = require('chalk')
|
|
64
|
+
const semver = require('semver')
|
|
58
65
|
const { buildBadgeHtml, DEFAULT_TOOLTIPS } = require('./badge')
|
|
59
66
|
|
|
60
67
|
const $enterpriseRegistry = Symbol('$enterpriseRegistry')
|
|
@@ -211,6 +218,81 @@ function reportUnreleasedFeature ({ feature, mode, filePath }) {
|
|
|
211
218
|
console.warn(chalk.yellow(message))
|
|
212
219
|
}
|
|
213
220
|
|
|
221
|
+
/**
|
|
222
|
+
* Parse a version string into a semver for comparison, or undefined when it
|
|
223
|
+
* cannot be parsed. Lenient the same way `semver.coerce` is: Redpanda docs
|
|
224
|
+
* versions are "MAJOR.MINOR", not full semver, so an exact-match parse would
|
|
225
|
+
* reject every one of them.
|
|
226
|
+
*/
|
|
227
|
+
function coerceVersion (raw) {
|
|
228
|
+
if (raw === undefined || raw === null) return undefined
|
|
229
|
+
return semver.coerce(String(raw)) || undefined
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* Whether a registry entry's `since` version has shipped as of the page
|
|
234
|
+
* being converted.
|
|
235
|
+
*
|
|
236
|
+
* enterprise-features.yml lives in the non-versioned 'shared' component, so
|
|
237
|
+
* every version branch reads the exact same entry -- unlike property data,
|
|
238
|
+
* which has one JSON file per release and is checked against the page's
|
|
239
|
+
* own version (see PROPERTY_AND_ENTERPRISE_REFERENCES.adoc). Without this
|
|
240
|
+
* check, a feature that ships in 26.3 renders as fully available -- linked,
|
|
241
|
+
* badge-free, no warning -- on a 24.1 page that predates it by two years.
|
|
242
|
+
*
|
|
243
|
+
* Returns true (shipped) when `since` is absent, unparsable, or the page's
|
|
244
|
+
* own version cannot be determined. That default favors data quality over
|
|
245
|
+
* release-readiness: unlike an unrecognized `status`, which fails closed
|
|
246
|
+
* because it is the one mistake that could publish an unreleased feature,
|
|
247
|
+
* a `since` typo or a build with no page-version context has no such
|
|
248
|
+
* asymmetry to protect against, so it defaults to not gating rather than
|
|
249
|
+
* hiding a shipped feature over unrelated bad data. An unparsable `since`
|
|
250
|
+
* is still reported, the same way an unrecognized `status` is: the failure
|
|
251
|
+
* mode differs (not gated, instead of gated), but a silent typo is exactly
|
|
252
|
+
* as easy to miss either way.
|
|
253
|
+
*
|
|
254
|
+
* @param {object} entry - Registry entry.
|
|
255
|
+
* @param {object} [config] - Extension config, used to read the page's own
|
|
256
|
+
* version.
|
|
257
|
+
* @param {object} [report] - {mode, filePath, contentCatalog} to report an
|
|
258
|
+
* unparsable since value. Omit to check silently.
|
|
259
|
+
*/
|
|
260
|
+
function isFeatureShippedOnPage (entry, config, report) {
|
|
261
|
+
const since = entry && entry.since
|
|
262
|
+
if (since === undefined || since === null || String(since).trim() === '') return true
|
|
263
|
+
const sinceVersion = coerceVersion(since)
|
|
264
|
+
if (!sinceVersion) {
|
|
265
|
+
if (report && report.mode !== 'off') {
|
|
266
|
+
const where = report.filePath ? ` in ${report.filePath}` : ''
|
|
267
|
+
const message =
|
|
268
|
+
`enterprise:${entry.name}[]${where}: registry has since: '${since}', which is not a parsable version, so the since-gating check is skipped and the feature renders as already shipped. ` +
|
|
269
|
+
'Fix the since value in enterprise-features.yml (e.g. "26.3"), or remove the key if the feature has always been available.'
|
|
270
|
+
if (report.contentCatalog) warnOnce(report.contentCatalog, `since:${entry.name}`, message)
|
|
271
|
+
else console.warn(chalk.yellow(message))
|
|
272
|
+
}
|
|
273
|
+
return true
|
|
274
|
+
}
|
|
275
|
+
const src = config && config.file && config.file.src
|
|
276
|
+
const pageVersion = src && coerceVersion(src.version)
|
|
277
|
+
if (!pageVersion) return true
|
|
278
|
+
return semver.gte(pageVersion, sinceVersion)
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
* Report an inline mention of a feature before its `since` version, on a
|
|
283
|
+
* page whose own version predates it.
|
|
284
|
+
*/
|
|
285
|
+
function reportFeatureNotYetShipped ({ feature, since, pageVersion, mode, filePath }) {
|
|
286
|
+
if (mode === 'off') return
|
|
287
|
+
const where = filePath ? ` in ${filePath}` : ''
|
|
288
|
+
const message =
|
|
289
|
+
`enterprise:${feature}[]${where}: '${feature}' is marked since: ${since} in the enterprise features registry, but this page is version ${pageVersion}, which predates it. ` +
|
|
290
|
+
'The mention renders as plain text with no enterprise styling, tooltip, or link. ' +
|
|
291
|
+
'Move the mention to a page whose version is at or after the since version, or remove since if the feature covers this version too.'
|
|
292
|
+
if (mode === 'error') throw new Error(message)
|
|
293
|
+
console.warn(chalk.yellow(message))
|
|
294
|
+
}
|
|
295
|
+
|
|
214
296
|
/**
|
|
215
297
|
* The badge for a registry entry, wrapped for use in generated AsciiDoc.
|
|
216
298
|
*
|
|
@@ -436,6 +518,8 @@ function buildEnterpriseContent ({ feature, text, xref, url, tooltip, licensingP
|
|
|
436
518
|
* @param {object} [opts]
|
|
437
519
|
* @param {string} [opts.title] - Table title override.
|
|
438
520
|
* @param {string} [opts.heading] - Third column heading override.
|
|
521
|
+
* @param {object} [opts.config] - Extension config, used to read the page's
|
|
522
|
+
* own version for since-gating (see isFeatureShippedOnPage).
|
|
439
523
|
* @returns {string}
|
|
440
524
|
*/
|
|
441
525
|
function buildFeatureTable (features, scope, opts = {}) {
|
|
@@ -456,7 +540,13 @@ function buildFeatureTable (features, scope, opts = {}) {
|
|
|
456
540
|
// did not fail there either.
|
|
457
541
|
.filter((feature) => {
|
|
458
542
|
const status = entryStatus(feature, report)
|
|
459
|
-
|
|
543
|
+
if (!includeUnreleased && status === STATUS_UNRELEASED) return false
|
|
544
|
+
// since only describes a shipped feature's first version, so it does
|
|
545
|
+
// not apply to one that has not released at all yet. Silent, like the
|
|
546
|
+
// unreleased filter above: an older version's table naturally does not
|
|
547
|
+
// list a feature that did not exist yet on that version, which is not
|
|
548
|
+
// a writer mistake worth warning about.
|
|
549
|
+
return status === STATUS_UNRELEASED || isFeatureShippedOnPage(feature, opts.config, report)
|
|
460
550
|
})
|
|
461
551
|
.sort((a, b) => a.name.localeCompare(b.name, 'en', { sensitivity: 'base' }))
|
|
462
552
|
const title = opts.title || TABLE_TITLES[scope]
|
|
@@ -517,6 +607,18 @@ function enterpriseInlineMacro (config) {
|
|
|
517
607
|
reportUnreleasedFeature({ feature, mode, filePath })
|
|
518
608
|
return self.createInline(parent, 'quoted', attributes.text || feature)
|
|
519
609
|
}
|
|
610
|
+
if (!isFeatureShippedOnPage(entry, config, { mode, filePath, contentCatalog: config && config.contentCatalog })) {
|
|
611
|
+
// isFeatureShippedOnPage only returns false when entry.since parsed,
|
|
612
|
+
// so entry is defined here.
|
|
613
|
+
reportFeatureNotYetShipped({
|
|
614
|
+
feature,
|
|
615
|
+
since: entry.since,
|
|
616
|
+
pageVersion: config && config.file && config.file.src && config.file.src.version,
|
|
617
|
+
mode,
|
|
618
|
+
filePath,
|
|
619
|
+
})
|
|
620
|
+
return self.createInline(parent, 'quoted', attributes.text || feature)
|
|
621
|
+
}
|
|
520
622
|
let content = buildEnterpriseContent({
|
|
521
623
|
feature,
|
|
522
624
|
text: attributes.text,
|
|
@@ -568,6 +670,7 @@ function enterpriseFeaturesBlockMacro (config) {
|
|
|
568
670
|
// Prerelease docs describe the upcoming release, so they list features
|
|
569
671
|
// that are still only in a release candidate.
|
|
570
672
|
includeUnreleased: isPrereleasePage(config, parent.getDocument()),
|
|
673
|
+
config,
|
|
571
674
|
})
|
|
572
675
|
// A block anchor before the macro call ([[my-id]]) is consumed into the
|
|
573
676
|
// macro's id attribute. Re-emit it so crossrefs to the table keep working.
|
|
@@ -598,3 +701,4 @@ module.exports.buildFeatureTable = buildFeatureTable
|
|
|
598
701
|
module.exports.parseRegistry = parseRegistry
|
|
599
702
|
module.exports.resolveEntryXref = resolveEntryXref
|
|
600
703
|
module.exports.resolveTooltipAttribute = resolveTooltipAttribute
|
|
704
|
+
module.exports.isFeatureShippedOnPage = isFeatureShippedOnPage
|
package/package.json
CHANGED
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
14
|
const yaml = require('js-yaml')
|
|
15
|
+
const semver = require('semver')
|
|
15
16
|
|
|
16
17
|
const VALID_SCOPES = ['redpanda', 'console', 'connect', 'operator', 'cloud']
|
|
17
18
|
const SOURCE_KINDS = ['core-enum', 'core-property', 'connect-plugin', 'manual']
|
|
@@ -58,6 +59,14 @@ function lintRegistry (source) {
|
|
|
58
59
|
} else if (!feature.source.value || !String(feature.source.value).trim()) {
|
|
59
60
|
findings.push(finding('error', 'registry-lint', `'${name}' has an empty source value; manual entries need a justification.`))
|
|
60
61
|
}
|
|
62
|
+
if (feature.since !== undefined && feature.since !== null && String(feature.since).trim() !== '') {
|
|
63
|
+
const since = String(feature.since).trim()
|
|
64
|
+
// Lenient the same way the enterprise macro's own coerceVersion is:
|
|
65
|
+
// docs versions are "MAJOR.MINOR", not full semver.
|
|
66
|
+
if (!semver.coerce(since)) {
|
|
67
|
+
findings.push(finding('error', 'registry-lint', `'${name}' has since '${since}', which is not a parsable version.`))
|
|
68
|
+
}
|
|
69
|
+
}
|
|
61
70
|
}
|
|
62
71
|
return { features: data.features, findings }
|
|
63
72
|
}
|