@redpanda-data/docs-extensions-and-macros 4.0.0 → 4.1.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/README.adoc
CHANGED
|
@@ -254,6 +254,90 @@ antora:
|
|
|
254
254
|
- require: '@redpanda-data/docs-extensions-and-macros/extensions/generate-rp-connect-categories'
|
|
255
255
|
```
|
|
256
256
|
|
|
257
|
+
=== Compute end-of-life extension
|
|
258
|
+
|
|
259
|
+
This extension calculates and attaches metadata related to the end-of-life (EoL) status of docs pages, such as nearing EoL, past EoL, and associated EoL dates. This metadata can be used to display relevant banners or messages in docs to inform users about the lifecycle of each version.
|
|
260
|
+
|
|
261
|
+
The extension leverages configuration settings provided in the Antora playbook to apply EoL calculations, specify the warning period, and include links to upgrade documentation and EoL policies.
|
|
262
|
+
|
|
263
|
+
The extension computes whether a page is nearing EoL or past EoL based on the `page-release-date` attribute and configured settings.
|
|
264
|
+
It injects the following attributes into each page, making them available for use in UI templates:
|
|
265
|
+
|
|
266
|
+
- `page-is-nearing-eol`: Indicates if the page is within the warning period before EoL. Calculated using `(page-release-date + supported_months) - warning_weeks`.
|
|
267
|
+
- `page-is-past-eol`: Indicates if the page has passed its EoL. Calculated using `today > (page-release-date + supported_months)`.
|
|
268
|
+
- `page-eol-date`: The calculated EoL date in a human-readable format. Calculated using `page-release-date + supported_months`.
|
|
269
|
+
- `page-eol-doc`: The URL to the supported versions policy or EoL documentation.
|
|
270
|
+
- `page-upgrade-doc`: The Antora resource ID to a document containing upgrade instructions.
|
|
271
|
+
|
|
272
|
+
==== Environment variables
|
|
273
|
+
|
|
274
|
+
This extension does not require any environment variables.
|
|
275
|
+
|
|
276
|
+
==== Configuration options
|
|
277
|
+
|
|
278
|
+
To enable and configure the extension, add it to the `antora.extensions` section of your Antora playbook. Define the EoL settings under the `data.eol_settings` key with the following options:
|
|
279
|
+
|
|
280
|
+
`component` (required):: The component name to which the configuration applies.
|
|
281
|
+
`eol_doc` (required):: A link to the supported versions policy or EoL documentation.
|
|
282
|
+
`upgrade_doc` (required):: A link to the upgrade instructions.
|
|
283
|
+
`supported_months` (optional, default: 12):: The number of months after the publish date when the documentation reaches its EoL.
|
|
284
|
+
`warning_weeks` (optional, default: 6):: The number of weeks before EoL when the documentation is considered to be nearing EoL. Can be used to decide when to notify users of the upcoming EoL status.
|
|
285
|
+
|
|
286
|
+
[,yaml]
|
|
287
|
+
----
|
|
288
|
+
antora:
|
|
289
|
+
extensions:
|
|
290
|
+
- require: '@redpanda-data/docs-extensions-and-macros/extensions/end-of-life'
|
|
291
|
+
data:
|
|
292
|
+
eol_settings:
|
|
293
|
+
- component: 'ROOT'
|
|
294
|
+
supported_months: 18
|
|
295
|
+
warning_weeks: 8
|
|
296
|
+
eol_doc: https://support.redpanda.com/hc/en-us/articles/20617574366743-Redpanda-Supported-Versions
|
|
297
|
+
upgrade_doc: ROOT:upgrade:index.adoc
|
|
298
|
+
----
|
|
299
|
+
|
|
300
|
+
==== Registration example
|
|
301
|
+
|
|
302
|
+
You can register the extension with a customized configuration for different components in your playbook:
|
|
303
|
+
|
|
304
|
+
[,yaml]
|
|
305
|
+
----
|
|
306
|
+
antora:
|
|
307
|
+
extensions:
|
|
308
|
+
- require: '@redpanda-data/docs-extensions-and-macros/extensions/compute-end-of-life'
|
|
309
|
+
data:
|
|
310
|
+
eol_settings:
|
|
311
|
+
- component: 'ROOT'
|
|
312
|
+
supported_months: 12
|
|
313
|
+
warning_weeks: 6
|
|
314
|
+
eol_doc: https://example.com/supported-versions
|
|
315
|
+
upgrade_doc: ROOT:upgrade:index.adoc
|
|
316
|
+
- component: 'example-docs'
|
|
317
|
+
supported_months: 24
|
|
318
|
+
warning_weeks: 12
|
|
319
|
+
eol_doc: https://example.com/example-supported-versions
|
|
320
|
+
upgrade_doc: example-docs:upgrade:index.adoc
|
|
321
|
+
----
|
|
322
|
+
|
|
323
|
+
|
|
324
|
+
==== Example Handlebars template:
|
|
325
|
+
|
|
326
|
+
[,handlebars]
|
|
327
|
+
----
|
|
328
|
+
{{#if page.attributes.is-nearing-eol}}
|
|
329
|
+
<div class="banner-container nearing-eol">
|
|
330
|
+
This documentation will reach its end of life on {{page.attributes.eol-date}}.
|
|
331
|
+
Please <a href="{{resolve-resource page.attributes.upgrade-doc}}">upgrade to a supported version</a>.
|
|
332
|
+
</div>
|
|
333
|
+
{{else if page.attributes.is-past-eol}}
|
|
334
|
+
<div class="banner-container past-eol">
|
|
335
|
+
This documentation reached its end of life on {{page.attributes.eol-date}}.
|
|
336
|
+
See our <a href="{{page.attributes.eol-doc}}" target="_blank">supported versions policy</a>.
|
|
337
|
+
</div>
|
|
338
|
+
{{/if}}
|
|
339
|
+
----
|
|
340
|
+
|
|
257
341
|
=== Generate index data
|
|
258
342
|
|
|
259
343
|
The `generate-index-data` extension creates structured index data about doc pages based on configurable filters. The indexed data is saved to a specified attribute in all component versions, enabling the dynamic generation of categorized links and descriptions within your docs using UI templates.
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const calculateEOL = require('./util/calculate-eol.js');
|
|
4
|
+
|
|
5
|
+
module.exports.register = function ({ config }) {
|
|
6
|
+
this.on('contentClassified', ({ contentCatalog }) => {
|
|
7
|
+
const logger = this.getLogger("compute-end-of-life-extension");
|
|
8
|
+
|
|
9
|
+
// Extract EOL configuration from the config object
|
|
10
|
+
const eolConfigs = config.data?.eol_settings || [];
|
|
11
|
+
if (!Array.isArray(eolConfigs) || eolConfigs.length === 0) {
|
|
12
|
+
logger.warn('No end-of-life settings found in configuration.');
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
eolConfigs.forEach(({ component: componentName, supported_months, warning_weeks, eol_doc, upgrade_doc }) => {
|
|
17
|
+
if (!eol_doc || !upgrade_doc) {
|
|
18
|
+
logger.error(
|
|
19
|
+
`End-of-life configuration for component "${component}" is missing required attributes. ` +
|
|
20
|
+
`Ensure both "eol_doc" and "upgrade_doc" are specified.`
|
|
21
|
+
);
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
const resolvedEOLMonths = supported_months && supported_months > 0 ? supported_months : 12; // Default: 12 months
|
|
25
|
+
const resolvedWarningWeeks = warning_weeks && warning_weeks > 0 ? warning_weeks : 6; // Default: 6 weeks
|
|
26
|
+
|
|
27
|
+
logger.info(
|
|
28
|
+
`Processing component: ${componentName} with end-of-life months: ${resolvedEOLMonths}, Warning weeks: ${resolvedWarningWeeks}`
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
const component = contentCatalog.getComponents().find((c) => c.name === componentName);
|
|
32
|
+
|
|
33
|
+
if (!component) {
|
|
34
|
+
logger.warn(`Component not found: ${componentName}`);
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
component.versions.forEach(({ asciidoc, version }) => {
|
|
39
|
+
const releaseDate = asciidoc.attributes['page-release-date'];
|
|
40
|
+
if (releaseDate) {
|
|
41
|
+
// Pass resolved configuration to calculateEOL
|
|
42
|
+
const eolInfo = calculateEOL(releaseDate, resolvedEOLMonths, resolvedWarningWeeks, logger);
|
|
43
|
+
Object.assign(asciidoc.attributes, {
|
|
44
|
+
'page-is-nearing-eol': eolInfo.isNearingEOL.toString(),
|
|
45
|
+
'page-is-past-eol': eolInfo.isPastEOL.toString(),
|
|
46
|
+
'page-eol-date': eolInfo.eolDate,
|
|
47
|
+
'page-eol-doc': eol_doc,
|
|
48
|
+
'page-upgrade-doc': upgrade_doc,
|
|
49
|
+
});
|
|
50
|
+
} else {
|
|
51
|
+
logger.warn(`No release date found for component: ${componentName}. Make sure to set {page-release-date} in the antora.yml of the component version ${version}.`);
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
};
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
module.exports = (releaseDate, eolMonths, warningWeeks, logger) => {
|
|
4
|
+
if (!releaseDate) {
|
|
5
|
+
logger.warn(
|
|
6
|
+
'No release date provided. Make sure to set {page-release-date} in the antora.yml of the component.'
|
|
7
|
+
);
|
|
8
|
+
return null;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
// Parse the input date string YYYY-MM-DD in UTC
|
|
12
|
+
const parseUTCDate = (dateString) => {
|
|
13
|
+
const [year, month, day] = dateString.split('-').map(Number);
|
|
14
|
+
// month - 1 because JS months are 0-based
|
|
15
|
+
// https://www.w3schools.com/jsref/jsref_getmonth.asp
|
|
16
|
+
return new Date(Date.UTC(year, month - 1, day));
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
const targetDate = parseUTCDate(releaseDate);
|
|
20
|
+
|
|
21
|
+
if (isNaN(targetDate.getTime())) {
|
|
22
|
+
logger.warn('Invalid release date format:', releaseDate);
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// Calculate EoL date in UTC
|
|
27
|
+
const eolDate = new Date(targetDate.getTime()); // clone
|
|
28
|
+
eolDate.setUTCMonth(eolDate.getUTCMonth() + eolMonths);
|
|
29
|
+
|
|
30
|
+
// Calculate the threshold for warning (X weeks before EoL) in UTC
|
|
31
|
+
const weeksBeforeEOL = new Date(eolDate.getTime());
|
|
32
|
+
weeksBeforeEOL.setUTCDate(weeksBeforeEOL.getUTCDate() - warningWeeks * 7);
|
|
33
|
+
|
|
34
|
+
// Compare times in milliseconds to avoid timezone confusion
|
|
35
|
+
const nowMs = Date.now();
|
|
36
|
+
const eolMs = eolDate.getTime();
|
|
37
|
+
const warningMs = weeksBeforeEOL.getTime();
|
|
38
|
+
|
|
39
|
+
const isNearingEOL = nowMs >= warningMs && nowMs < eolMs;
|
|
40
|
+
const isPastEOL = nowMs > eolMs;
|
|
41
|
+
|
|
42
|
+
// Format the EoL date in UTC
|
|
43
|
+
const humanReadableEOLDate = new Intl.DateTimeFormat('en-US', {
|
|
44
|
+
year: 'numeric',
|
|
45
|
+
month: 'long',
|
|
46
|
+
day: 'numeric',
|
|
47
|
+
timeZone: 'UTC', // Ensure UTC in output
|
|
48
|
+
}).format(eolDate);
|
|
49
|
+
|
|
50
|
+
return {
|
|
51
|
+
isNearingEOL,
|
|
52
|
+
isPastEOL,
|
|
53
|
+
eolDate: humanReadableEOLDate, // For example "March 1, 2025"
|
|
54
|
+
};
|
|
55
|
+
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
function customStringify(obj) {
|
|
2
|
+
return JSON.stringify(obj, (key, value) => {
|
|
3
|
+
if (value instanceof Map) {
|
|
4
|
+
return {
|
|
5
|
+
type: 'Map',
|
|
6
|
+
value: Array.from(value.entries())
|
|
7
|
+
};
|
|
8
|
+
} else if (value instanceof Set) {
|
|
9
|
+
return {
|
|
10
|
+
type: 'Set',
|
|
11
|
+
value: Array.from(value)
|
|
12
|
+
};
|
|
13
|
+
} else if (typeof value === 'function') {
|
|
14
|
+
return value.toString();
|
|
15
|
+
} else {
|
|
16
|
+
return value;
|
|
17
|
+
}
|
|
18
|
+
}, 2);
|
|
19
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@redpanda-data/docs-extensions-and-macros",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.1.0",
|
|
4
4
|
"description": "Antora extensions and macros developed for Redpanda documentation.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"antora",
|
|
@@ -33,6 +33,7 @@
|
|
|
33
33
|
"./extensions/archive-attachments": "./extensions/archive-attachments.js",
|
|
34
34
|
"./extensions/add-pages-to-root": "./extensions/add-pages-to-root.js",
|
|
35
35
|
"./extensions/collect-bloblang-samples": "./extensions/collect-bloblang-samples.js",
|
|
36
|
+
"./extensions/compute-end-of-life": "./extensions/compute-end-of-life.js",
|
|
36
37
|
"./extensions/generate-rp-connect-categories": "./extensions/generate-rp-connect-categories.js",
|
|
37
38
|
"./extensions/generate-index-data": "./extensions/generate-index-data.js",
|
|
38
39
|
"./extensions/generate-rp-connect-info": "./extensions/generate-rp-connect-info.js",
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
function customStringify(obj) {
|
|
2
|
-
return JSON.stringify(obj, (key, value) => {
|
|
3
|
-
if (value instanceof Map) {
|
|
4
|
-
return {
|
|
5
|
-
type: 'Map',
|
|
6
|
-
value: Array.from(value.entries())
|
|
7
|
-
};
|
|
8
|
-
} else if (value instanceof Set) {
|
|
9
|
-
return {
|
|
10
|
-
type: 'Set',
|
|
11
|
-
value: Array.from(value)
|
|
12
|
-
};
|
|
13
|
-
} else if (typeof value === 'function') {
|
|
14
|
-
return value.toString();
|
|
15
|
-
} else {
|
|
16
|
-
return value;
|
|
17
|
-
}
|
|
18
|
-
}, 2);
|
|
19
|
-
}
|