@redpanda-data/docs-extensions-and-macros 3.1.4 → 3.2.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 +73 -2
- package/extensions/add-global-attributes.js +11 -11
- package/extensions/algolia-indexer/generate-index.js +20 -9
- package/extensions/algolia-indexer/index.js +1 -1
- package/extensions/find-related-docs.js +70 -0
- package/extensions/find-related-labs.js +52 -0
- package/extensions/replace-attributes-in-attachments.js +18 -3
- package/extensions/unlisted-pages.js +1 -0
- package/extensions/validate-attributes.js +88 -0
- package/extensions/version-fetcher/{getLatestConsoleVersion.js → get-latest-console-version.js} +8 -11
- package/extensions/version-fetcher/get-latest-operator-version.js +28 -0
- package/extensions/version-fetcher/get-latest-redpanda-version.js +39 -0
- package/extensions/version-fetcher/set-latest-version.js +18 -6
- package/package.json +1 -1
- package/extensions/version-fetcher/getLatestRedpandaVersion.js +0 -46
package/README.adoc
CHANGED
|
@@ -79,9 +79,11 @@ antora:
|
|
|
79
79
|
|
|
80
80
|
=== Version fetcher
|
|
81
81
|
|
|
82
|
-
This extension fetches the latest release
|
|
82
|
+
This extension fetches the latest release versions from GitHub.
|
|
83
83
|
|
|
84
|
-
|
|
84
|
+
The latest Console version is made available to all versions of the Redpanda docs (`ROOT` component.)
|
|
85
|
+
|
|
86
|
+
The latest Redpanda version and the latest Redpanda Operator version is made available to the latest version of the `ROOT` component.
|
|
85
87
|
|
|
86
88
|
==== Environment variables
|
|
87
89
|
|
|
@@ -97,6 +99,75 @@ antora:
|
|
|
97
99
|
- '@redpanda-data/docs-extensions-and-macros/extensions/version-fetcher/set-latest-version'
|
|
98
100
|
```
|
|
99
101
|
|
|
102
|
+
=== Validate attributes
|
|
103
|
+
|
|
104
|
+
This extension ensures the consistency and validity of page attributes, focusing on validating page categories against a predefined list of valid categories and subcategories. It automatically adds missing parent categories for any specified subcategories and removes any specified categories that are invalid. Additionally, it processes specific environment attributes, setting corresponding page-level attributes when environment conditions are met.
|
|
105
|
+
|
|
106
|
+
==== Environment variables
|
|
107
|
+
|
|
108
|
+
This extension does not require any environment variables.
|
|
109
|
+
|
|
110
|
+
==== Configuration options
|
|
111
|
+
|
|
112
|
+
There are no configurable options for this extension. It operates based on site attributes defined in `add-global-attributes.js` to determine valid categories and subcategories.
|
|
113
|
+
|
|
114
|
+
==== Registration example
|
|
115
|
+
|
|
116
|
+
Register the `validate-attributes` extension in the Antora playbook under the `antora.extensions` key like so:
|
|
117
|
+
|
|
118
|
+
[source,yaml]
|
|
119
|
+
----
|
|
120
|
+
antora:
|
|
121
|
+
extensions:
|
|
122
|
+
- require: '@redpanda-data/docs-extensions-and-macros/extensions/validate-attributes.js'
|
|
123
|
+
----
|
|
124
|
+
|
|
125
|
+
=== Related docs
|
|
126
|
+
|
|
127
|
+
This extension enhances the connectivity between lab exercises and relevant documentation by dynamically identifying and linking related documentation pages and other lab exercises based on shared categories and deployment types.
|
|
128
|
+
|
|
129
|
+
==== Environment variables
|
|
130
|
+
|
|
131
|
+
This extension operates without requiring any specific environment variables.
|
|
132
|
+
|
|
133
|
+
==== Configuration options
|
|
134
|
+
|
|
135
|
+
This extension does not offer configurable options. It uses the inherent attributes of pages to determine relationships based on `page-categories` and deployment types (`env-kubernetes`, `env-linux`, `env-docker`, `page-cloud`).
|
|
136
|
+
|
|
137
|
+
==== Registration example
|
|
138
|
+
|
|
139
|
+
To integrate the `related-docs-extension` into your Antora playbook, add it under the `antora.extensions` key as demonstrated below:
|
|
140
|
+
|
|
141
|
+
[source,yaml]
|
|
142
|
+
----
|
|
143
|
+
antora:
|
|
144
|
+
extensions:
|
|
145
|
+
- require: '@redpanda-data/docs-extensions-and-macros/extensions/related-docs-extension.js'
|
|
146
|
+
----
|
|
147
|
+
|
|
148
|
+
=== Related labs
|
|
149
|
+
|
|
150
|
+
This extension enriches documentation pages with links to related lab exercises, facilitating a deeper understanding of the content through practical application. It dynamically assigns related labs to each documentation page based on shared categories and deployment types.
|
|
151
|
+
|
|
152
|
+
==== Environment variables
|
|
153
|
+
|
|
154
|
+
This extension does not require any environment variables.
|
|
155
|
+
|
|
156
|
+
==== Configuration options
|
|
157
|
+
|
|
158
|
+
The extension operates without explicit configuration options. It automatically processes documentation pages to identify and link related labs based on shared `page-categories` attributes and deployment types (`env-kubernetes`, `env-linux`, `env-docker`, `page-cloud`).
|
|
159
|
+
|
|
160
|
+
==== Registration example
|
|
161
|
+
|
|
162
|
+
Include the `related-labs-extension` in the Antora playbook under the `antora.extensions` key as follows:
|
|
163
|
+
|
|
164
|
+
[source,yaml]
|
|
165
|
+
----
|
|
166
|
+
antora:
|
|
167
|
+
extensions:
|
|
168
|
+
- require: '@redpanda-data/docs-extensions-and-macros/extensions/related-labs-extension.js'
|
|
169
|
+
----
|
|
170
|
+
|
|
100
171
|
=== Global attributes
|
|
101
172
|
|
|
102
173
|
This extension collects Asciidoc attributes from the {url-playbook}[`shared` component] and makes them available to all component versions. Having global attributes is useful for consistent configuration of local and production builds.
|
|
@@ -11,31 +11,31 @@ module.exports.register = function ({ config }) {
|
|
|
11
11
|
const _ = require('lodash');
|
|
12
12
|
|
|
13
13
|
this.on('contentAggregated', ({ siteCatalog, contentAggregate }) => {
|
|
14
|
-
let attributeFile;
|
|
15
14
|
try {
|
|
16
15
|
for (const component of contentAggregate) {
|
|
17
16
|
if (component.name === 'shared') {
|
|
18
|
-
|
|
19
|
-
if (!
|
|
20
|
-
logger.warn("No attributes
|
|
17
|
+
const attributeFiles = component.files.filter(file => file.path.startsWith('modules/ROOT/partials/') && file.path.endsWith('.yml'));
|
|
18
|
+
if (!attributeFiles.length) {
|
|
19
|
+
logger.warn("No YAML attributes files found in 'shared' component.");
|
|
21
20
|
} else {
|
|
22
|
-
siteCatalog.attributeFile =
|
|
23
|
-
|
|
21
|
+
siteCatalog.attributeFile = attributeFiles.reduce((acc, file) => {
|
|
22
|
+
const fileAttributes = yaml.load(file.contents.toString('utf8'));
|
|
23
|
+
return _.merge(acc, fileAttributes);
|
|
24
|
+
}, {});
|
|
25
|
+
console.log(chalk.green('Loaded global attributes from shared component.'));
|
|
24
26
|
}
|
|
25
|
-
break
|
|
27
|
+
break;
|
|
26
28
|
}
|
|
27
29
|
}
|
|
28
30
|
} catch (error) {
|
|
29
31
|
logger.error(`Error loading attributes: ${error.message}`);
|
|
30
32
|
}
|
|
31
33
|
})
|
|
32
|
-
.on('contentClassified', async ({ siteCatalog,contentCatalog }) => {
|
|
34
|
+
.on('contentClassified', async ({ siteCatalog, contentCatalog }) => {
|
|
33
35
|
const components = await contentCatalog.getComponents();
|
|
34
36
|
for (let i = 0; i < components.length; i++) {
|
|
35
37
|
let component = components[i];
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
component.versions.forEach(({asciidoc}) => {
|
|
38
|
+
component.versions.forEach(({ asciidoc }) => {
|
|
39
39
|
if (siteCatalog.attributeFile) {
|
|
40
40
|
asciidoc.attributes = _.merge({}, siteCatalog.attributeFile, asciidoc.attributes);
|
|
41
41
|
}
|
|
@@ -27,7 +27,6 @@ function generateIndex (playbook, contentCatalog, { indexLatestOnly = false, exc
|
|
|
27
27
|
const algolia = {}
|
|
28
28
|
|
|
29
29
|
console.log(chalk.cyan('Indexing...'))
|
|
30
|
-
|
|
31
30
|
const unixTimestamp = Math.floor(Date.now() / 1000)
|
|
32
31
|
|
|
33
32
|
// Select indexable pages
|
|
@@ -176,27 +175,39 @@ function generateIndex (playbook, contentCatalog, { indexLatestOnly = false, exc
|
|
|
176
175
|
.replace(/\s+/g, ' ')
|
|
177
176
|
.trim();
|
|
178
177
|
|
|
179
|
-
var tag = `${component.title}
|
|
178
|
+
var tag = `${component.title}${version ? '-' + version : ''}`
|
|
179
|
+
var indexItem;
|
|
180
|
+
const deployment = page.asciidoc?.attributes['env-kubernetes'] ? 'Kubernetes' : page.asciidoc?.attributes['env-linux'] ? 'Linux' : page.asciidoc?.attributes['env-docker'] ? 'Docker' : page.asciidoc?.attributes['page-cloud'] ? 'Redpanda Cloud' : ''
|
|
181
|
+
|
|
182
|
+
const categories = page.asciidoc?.attributes['page-categories']
|
|
183
|
+
? page.asciidoc.attributes['page-categories'].split(',').map(category => category.trim())
|
|
184
|
+
: []
|
|
180
185
|
|
|
181
|
-
|
|
186
|
+
var indexItem = {
|
|
182
187
|
title: documentTitle,
|
|
183
|
-
product: component.title,
|
|
184
188
|
version: version,
|
|
185
189
|
text: text,
|
|
186
|
-
breadcrumbs: breadcrumbs,
|
|
187
190
|
intro: intro,
|
|
188
191
|
objectID: urlPath + page.pub.url,
|
|
189
192
|
titles: titles,
|
|
190
|
-
|
|
193
|
+
categories: categories,
|
|
191
194
|
unixTimestamp: unixTimestamp,
|
|
192
|
-
type: 'Doc',
|
|
193
|
-
_tags: [tag, 'docs']
|
|
194
195
|
}
|
|
195
196
|
|
|
197
|
+
if (component.name !== 'redpanda-labs') {
|
|
198
|
+
indexItem.product = component.title;
|
|
199
|
+
indexItem.breadcrumbs = breadcrumbs;
|
|
200
|
+
indexItem.type = 'Doc';
|
|
201
|
+
indexItem._tags = [tag, 'docs'];
|
|
202
|
+
} else {
|
|
203
|
+
indexItem.deployment = deployment;
|
|
204
|
+
indexItem.type = 'Lab';
|
|
205
|
+
indexItem.interactive = false;
|
|
206
|
+
indexItem._tags = ['labs'];
|
|
207
|
+
}
|
|
196
208
|
algolia[cname][version].push(indexItem)
|
|
197
209
|
algoliaCount++
|
|
198
210
|
}
|
|
199
|
-
|
|
200
211
|
return algolia
|
|
201
212
|
}
|
|
202
213
|
|
|
@@ -114,7 +114,7 @@ function register({
|
|
|
114
114
|
}
|
|
115
115
|
|
|
116
116
|
for (const [objectID, obj] of existingObjectsMap) {
|
|
117
|
-
if (obj.type === 'Doc' && !obj._tags.includes('apis') || !obj.type) {
|
|
117
|
+
if ((obj.type === 'Doc' && !obj._tags.includes('apis')) || (!obj.type) || (obj.type === 'Lab' && !obj.interactive)) {
|
|
118
118
|
objectsToDelete.push(objectID)
|
|
119
119
|
}
|
|
120
120
|
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
module.exports.register = function ({ config }) {
|
|
4
|
+
const logger = this.getLogger('related-docs-extension');
|
|
5
|
+
|
|
6
|
+
this.on('documentsConverted', async ({ contentCatalog, siteCatalog }) => {
|
|
7
|
+
const labs = contentCatalog.findBy({ component: 'redpanda-labs', family: 'page' });
|
|
8
|
+
labs.forEach((labPage) => {
|
|
9
|
+
const relatedDocs = []
|
|
10
|
+
const relatedLabs = []
|
|
11
|
+
const sourceAttributes = labPage.asciidoc.attributes
|
|
12
|
+
const pageCategories = sourceAttributes['page-categories'];
|
|
13
|
+
if (!pageCategories) return;
|
|
14
|
+
const sourceCategoryList = pageCategories.split(',').map(c => c.trim());
|
|
15
|
+
const sourceDeploymentType = getDeploymentType(sourceAttributes)
|
|
16
|
+
const docs = contentCatalog.findBy({ component: 'ROOT', family: 'page' });
|
|
17
|
+
docs.forEach((docPage) => {
|
|
18
|
+
const related = findRelated(docPage, sourceCategoryList, sourceDeploymentType, logger)
|
|
19
|
+
related && relatedDocs.push(related)
|
|
20
|
+
})
|
|
21
|
+
labs.forEach((targetLabPage) => {
|
|
22
|
+
if (targetLabPage === labPage) return;
|
|
23
|
+
|
|
24
|
+
const related = findRelated(targetLabPage, sourceCategoryList, sourceDeploymentType, logger);
|
|
25
|
+
if (related) relatedLabs.push(related);
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
// Store related docs and labs in the lab page attributes
|
|
29
|
+
if (relatedDocs.length > 0) {
|
|
30
|
+
labPage.asciidoc.attributes['page-related-docs'] = JSON.stringify(relatedDocs);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if (relatedLabs.length > 0) {
|
|
34
|
+
labPage.asciidoc.attributes['page-related-labs'] = JSON.stringify(relatedLabs);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (relatedDocs.length > 0 || relatedLabs.length > 0) {
|
|
38
|
+
logger.info(`Set related docs and labs attributes for ${labPage.asciidoc.doctitle}`);
|
|
39
|
+
}
|
|
40
|
+
})
|
|
41
|
+
})
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function findRelated(docPage, sourceCategoryList, sourceDeploymentType, logger) {
|
|
45
|
+
const targetAttributes = docPage.asciidoc.attributes
|
|
46
|
+
const pageCategories = targetAttributes['page-categories'];
|
|
47
|
+
if (!pageCategories) return null;
|
|
48
|
+
const targetCategoryList = pageCategories.split(',').map(c => c.trim());
|
|
49
|
+
const targetDeploymentType = getDeploymentType(targetAttributes)
|
|
50
|
+
const categoryMatch = hasMatchingCategory(sourceCategoryList, targetCategoryList)
|
|
51
|
+
if (categoryMatch && (!targetDeploymentType ||sourceDeploymentType === targetDeploymentType)) {
|
|
52
|
+
return {
|
|
53
|
+
title: docPage.asciidoc.doctitle,
|
|
54
|
+
url: docPage.pub.url,
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return null
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function getDeploymentType (attributes) {
|
|
61
|
+
return attributes['env-kubernetes'] ? 'Kubernetes'
|
|
62
|
+
: attributes['env-linux'] ? 'Linux'
|
|
63
|
+
: attributes['env-docker'] ? 'Docker'
|
|
64
|
+
: attributes.cloud ? 'Redpanda Cloud'
|
|
65
|
+
: ''
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function hasMatchingCategory (sourcePageCategories, targetPageCategories) {
|
|
69
|
+
return sourcePageCategories.every((category) => targetPageCategories.includes(category))
|
|
70
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
module.exports.register = function ({ config }) {
|
|
4
|
+
const logger = this.getLogger('related-labs-extension');
|
|
5
|
+
|
|
6
|
+
this.on('documentsConverted', async ({ contentCatalog, siteCatalog }) => {
|
|
7
|
+
const docs = contentCatalog.findBy({ component: 'ROOT', family: 'page' });
|
|
8
|
+
docs.forEach((docPage) => {
|
|
9
|
+
const relatedLabs = []
|
|
10
|
+
const sourceAttributes = docPage.asciidoc.attributes
|
|
11
|
+
const pageCategories = sourceAttributes['page-categories'];
|
|
12
|
+
if (!pageCategories) return;
|
|
13
|
+
const sourceCategoryList = pageCategories.split(',').map(c => c.trim());
|
|
14
|
+
const sourceDeploymentType = getDeploymentType(sourceAttributes)
|
|
15
|
+
const labs = contentCatalog.findBy({ component: 'redpanda-labs', family: 'page' });
|
|
16
|
+
labs.forEach((labPage) => {
|
|
17
|
+
const related = findRelated(labPage, sourceCategoryList, sourceDeploymentType, logger)
|
|
18
|
+
related && relatedLabs.push(related)
|
|
19
|
+
})
|
|
20
|
+
if (!relatedLabs.length) return
|
|
21
|
+
docPage.asciidoc.attributes['page-related-labs'] = JSON.stringify(relatedLabs)
|
|
22
|
+
logger.info(`Set page-related-labs attribute for ${docPage.asciidoc.doctitle} to ${docPage.asciidoc.attributes['page-related-labs']}`)
|
|
23
|
+
})
|
|
24
|
+
})
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function findRelated(labPage, sourceCategoryList, sourceDeploymentType, logger) {
|
|
28
|
+
const targetAttributes = labPage.asciidoc.attributes
|
|
29
|
+
const pageCategories = targetAttributes['page-categories'];
|
|
30
|
+
if (!pageCategories) return null;
|
|
31
|
+
const targetCategoryList = pageCategories.split(',').map(c => c.trim());
|
|
32
|
+
const targetDeploymentType = getDeploymentType(targetAttributes)
|
|
33
|
+
const categoryMatch = hasMatchingCategory(sourceCategoryList, targetCategoryList)
|
|
34
|
+
if (categoryMatch && (!targetDeploymentType ||sourceDeploymentType === targetDeploymentType || targetDeploymentType === 'Docker')) {
|
|
35
|
+
return {
|
|
36
|
+
title: labPage.asciidoc.doctitle,
|
|
37
|
+
url: labPage.pub.url,
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
return null
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function getDeploymentType (attributes) {
|
|
44
|
+
return attributes['env-kubernetes'] ? 'Kubernetes'
|
|
45
|
+
: attributes['env-linux'] ? 'Linux'
|
|
46
|
+
: attributes['env-docker'] ? 'Docker'
|
|
47
|
+
: attributes.cloud ? 'Redpanda Cloud' : ''
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function hasMatchingCategory (sourcePageCategories, targetPageCategories) {
|
|
51
|
+
return sourcePageCategories.every((category) => targetPageCategories.includes(category))
|
|
52
|
+
}
|
|
@@ -4,19 +4,34 @@ module.exports.register = function ({ config }) {
|
|
|
4
4
|
|
|
5
5
|
const sanitizeAttributeValue = (value) => String(value).replace("@", "");
|
|
6
6
|
|
|
7
|
-
this.on('
|
|
7
|
+
this.on('contentClassified', ({contentCatalog}) => {
|
|
8
8
|
for (const { versions } of contentCatalog.getComponents()) {
|
|
9
9
|
for (const { name: component, version, asciidoc } of versions) {
|
|
10
10
|
const attachments = contentCatalog.findBy({ component, version, family });
|
|
11
|
-
if (component == 'api') continue
|
|
11
|
+
if (component == 'api') continue;
|
|
12
12
|
for (const attachment of attachments) {
|
|
13
13
|
let contentString = attachment['_contents'].toString('utf8');
|
|
14
|
-
if (!asciidoc.attributes) continue
|
|
14
|
+
if (!asciidoc.attributes) continue;
|
|
15
|
+
|
|
16
|
+
// Replace general attributes
|
|
15
17
|
for (const key in asciidoc.attributes) {
|
|
16
18
|
const placeholder = "{" + key + "}";
|
|
17
19
|
const sanitizedValue = sanitizeAttributeValue(asciidoc.attributes[key]);
|
|
18
20
|
contentString = contentString.replace(new RegExp(placeholder, 'g'), sanitizedValue);
|
|
19
21
|
}
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
// Specific replacements for YAML files
|
|
25
|
+
if (attachment.out.path.endsWith('.yaml') || attachment.out.path.endsWith('.yml')) {
|
|
26
|
+
const redpandaVersionRegex = /(\$\{REDPANDA_VERSION[^\}]*\})/g;
|
|
27
|
+
const redpandaConsoleVersionRegex = /(\$\{REDPANDA_CONSOLE_VERSION[^\}]*\})/g;
|
|
28
|
+
const fullVersion = asciidoc.attributes['full-version'] ? sanitizeAttributeValue(asciidoc.attributes['full-version']) : '';
|
|
29
|
+
const latestConsoleVersion = asciidoc.attributes['latest-console-version'] ? sanitizeAttributeValue(asciidoc.attributes['latest-console-version']) : '';
|
|
30
|
+
|
|
31
|
+
contentString = contentString.replace(redpandaVersionRegex, fullVersion);
|
|
32
|
+
contentString = contentString.replace(redpandaConsoleVersionRegex, latestConsoleVersion);
|
|
33
|
+
}
|
|
34
|
+
|
|
20
35
|
attachment['_contents'] = Buffer.from(contentString, "utf-8");
|
|
21
36
|
}
|
|
22
37
|
}
|
|
@@ -6,6 +6,7 @@ module.exports.register = function ({ config }) {
|
|
|
6
6
|
contentCatalog.getComponents().forEach(({ versions }) => {
|
|
7
7
|
versions.forEach(({ name: component, version, navigation: nav, url: defaultUrl }) => {
|
|
8
8
|
if (component === 'api') return;
|
|
9
|
+
if (!nav) return
|
|
9
10
|
const navEntriesByUrl = getNavEntriesByUrl(nav)
|
|
10
11
|
const unlistedPages = contentCatalog
|
|
11
12
|
.findBy({ component, version, family: 'page' })
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/* Example use in the playbook
|
|
2
|
+
* antora:
|
|
3
|
+
extensions:
|
|
4
|
+
* - require: ./extensions/validate-attributes.js
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
'use strict';
|
|
8
|
+
|
|
9
|
+
module.exports.register = function ({ config }) {
|
|
10
|
+
const logger = this.getLogger('attribute-validation-extension');
|
|
11
|
+
|
|
12
|
+
this.on('documentsConverted', async ({ contentCatalog, siteCatalog }) => {
|
|
13
|
+
// Retrieve valid categories and subcategories from site attributes defined in add-global-attributes.js.
|
|
14
|
+
const validCategories = siteCatalog.attributeFile['page-valid-categories'];
|
|
15
|
+
const categoryMap = createCategoryMap(validCategories);
|
|
16
|
+
const pages = contentCatalog.findBy({ family: 'page' });
|
|
17
|
+
pages.forEach((page) => {
|
|
18
|
+
let pageCategories = page.asciidoc.attributes['page-categories'];
|
|
19
|
+
if (!pageCategories) return;
|
|
20
|
+
let pageCategoryList = pageCategories.split(',').map(c => c.trim());
|
|
21
|
+
const validatedCategories = validateCategories(pageCategoryList, page.asciidoc.attributes['page-relative-src-path'], categoryMap, logger);
|
|
22
|
+
page.asciidoc.attributes['page-categories'] = validatedCategories
|
|
23
|
+
processEnvironmentAttributes(page, logger);
|
|
24
|
+
})
|
|
25
|
+
})
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function processEnvironmentAttributes(page, logger) {
|
|
29
|
+
const envAttributes = ['env-kubernetes', 'env-linux', 'env-docker'];
|
|
30
|
+
envAttributes.forEach(envAttr => {
|
|
31
|
+
if (page.asciidoc.attributes[envAttr]) {
|
|
32
|
+
// If the env attribute exists, set a corresponding page- attribute for use in the UI
|
|
33
|
+
const pageEnvAttr = `page-${envAttr}`;
|
|
34
|
+
page.asciidoc.attributes[pageEnvAttr] = true;
|
|
35
|
+
logger.info(`Set '${pageEnvAttr}' for ${page.asciidoc.attributes['page-relative-src-path']}`);
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function validateCategories(pageCategoryList, pageInfo, categoryMap, logger) {
|
|
41
|
+
let isValid = true;
|
|
42
|
+
let adjustedCategories = new Set(pageCategoryList);
|
|
43
|
+
|
|
44
|
+
pageCategoryList.forEach(category => {
|
|
45
|
+
// Check if the current category is a subcategory
|
|
46
|
+
if (categoryMap.subcategories.has(category)) {
|
|
47
|
+
// Retrieve the parent category for the current subcategory
|
|
48
|
+
const parentCategory = categoryMap.parentMap.get(category);
|
|
49
|
+
|
|
50
|
+
// Check if the parent category is not already in the pageCategoryList
|
|
51
|
+
if (!adjustedCategories.has(parentCategory)) {
|
|
52
|
+
// Add the parent category since it's missing
|
|
53
|
+
adjustedCategories.add(parentCategory);
|
|
54
|
+
logger.info(`Added missing parent category '${parentCategory}' for subcategory '${category}' in ${pageInfo}`);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
// Check if the current category is not a valid category or subcategory
|
|
58
|
+
else if (!categoryMap.categories.has(category)) {
|
|
59
|
+
logger.warn(`Invalid category '${category}' in ${pageInfo}`);
|
|
60
|
+
adjustedCategories.delete(category)
|
|
61
|
+
isValid = false;
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
if (!isValid) {
|
|
66
|
+
logger.warn(`Invalid categories detected. For a list of valid categories, see https://github.com/redpanda-data/docs/tree/shared/modules/ROOT/partials/valid-categories.yml`);
|
|
67
|
+
}
|
|
68
|
+
return Array.from(adjustedCategories).join(', ');
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function createCategoryMap(validCategories) {
|
|
72
|
+
const categoryMap = {
|
|
73
|
+
categories: new Set(),
|
|
74
|
+
subcategories: new Set(),
|
|
75
|
+
parentMap: new Map()
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
validCategories.forEach(categoryInfo => {
|
|
79
|
+
categoryMap.categories.add(categoryInfo.category);
|
|
80
|
+
if (categoryInfo.subcategories) {
|
|
81
|
+
categoryInfo.subcategories.forEach(subcat => {
|
|
82
|
+
categoryMap.subcategories.add(subcat.category);
|
|
83
|
+
categoryMap.parentMap.set(subcat.category, categoryInfo.category);
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
return categoryMap
|
|
88
|
+
}
|
package/extensions/version-fetcher/{getLatestConsoleVersion.js → get-latest-console-version.js}
RENAMED
|
@@ -19,15 +19,12 @@ const github = new OctokitWithRetries(githubOptions);
|
|
|
19
19
|
var latestConsoleReleaseVersion;
|
|
20
20
|
|
|
21
21
|
module.exports = async () => {
|
|
22
|
-
|
|
23
|
-
owner,
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
return null
|
|
31
|
-
}))
|
|
32
|
-
return latestConsoleReleaseVersion;
|
|
22
|
+
try {
|
|
23
|
+
const release = await github.rest.repos.getLatestRelease({ owner, repo });
|
|
24
|
+
latestConsoleReleaseVersion = release.data.tag_name.replace('v','');
|
|
25
|
+
return latestConsoleReleaseVersion;
|
|
26
|
+
} catch (error) {
|
|
27
|
+
console.error(error);
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
33
30
|
};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
// Fetch the latest release version from GitHub
|
|
2
|
+
const { Octokit } = require("@octokit/rest");
|
|
3
|
+
const { retry } = require("@octokit/plugin-retry");
|
|
4
|
+
const OctokitWithRetries = Octokit.plugin(retry);
|
|
5
|
+
const owner = 'redpanda-data';
|
|
6
|
+
const repo = 'redpanda-operator';
|
|
7
|
+
|
|
8
|
+
let githubOptions = {
|
|
9
|
+
userAgent: 'Redpanda Docs',
|
|
10
|
+
baseUrl: 'https://api.github.com',
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
if (process.env.REDPANDA_GITHUB_TOKEN) {
|
|
14
|
+
githubOptions.auth = process.env.REDPANDA_GITHUB_TOKEN;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const github = new OctokitWithRetries(githubOptions);
|
|
18
|
+
|
|
19
|
+
module.exports = async () => {
|
|
20
|
+
try {
|
|
21
|
+
const release = await github.rest.repos.getLatestRelease({ owner, repo });
|
|
22
|
+
latestOperatorReleaseVersion = release.data.tag_name;
|
|
23
|
+
return latestOperatorReleaseVersion;
|
|
24
|
+
} catch (error) {
|
|
25
|
+
console.error(error);
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
};
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
// Fetch the latest release version from GitHub
|
|
2
|
+
const { Octokit } = require("@octokit/rest");
|
|
3
|
+
const { retry } = require("@octokit/plugin-retry");
|
|
4
|
+
const OctokitWithRetries = Octokit.plugin(retry);
|
|
5
|
+
const owner = 'redpanda-data';
|
|
6
|
+
const repo = 'redpanda';
|
|
7
|
+
|
|
8
|
+
let githubOptions = {
|
|
9
|
+
userAgent: 'Redpanda Docs',
|
|
10
|
+
baseUrl: 'https://api.github.com',
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
if (process.env.REDPANDA_GITHUB_TOKEN) {
|
|
14
|
+
githubOptions.auth = process.env.REDPANDA_GITHUB_TOKEN;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const github = new OctokitWithRetries(githubOptions);
|
|
18
|
+
|
|
19
|
+
module.exports = async () => {
|
|
20
|
+
try {
|
|
21
|
+
// Fetch the latest release
|
|
22
|
+
const release = await github.rest.repos.getLatestRelease({ owner, repo });
|
|
23
|
+
const tag = release.data.tag_name;
|
|
24
|
+
latestRedpandaReleaseVersion = tag.replace('v', '');
|
|
25
|
+
|
|
26
|
+
// Get reference of the tag
|
|
27
|
+
const tagRef = await github.rest.git.getRef({ owner, repo, ref: `tags/${tag}` });
|
|
28
|
+
const releaseSha = tagRef.data.object.sha;
|
|
29
|
+
|
|
30
|
+
// Get the tag object to extract the commit hash
|
|
31
|
+
const tagData = await github.rest.git.getTag({ owner, repo, tag_sha: releaseSha });
|
|
32
|
+
latestRedpandaReleaseCommitHash = tagData.data.object.sha.substring(0, 7);
|
|
33
|
+
|
|
34
|
+
return [latestRedpandaReleaseVersion, latestRedpandaReleaseCommitHash];
|
|
35
|
+
} catch (error) {
|
|
36
|
+
console.error(error);
|
|
37
|
+
return [null, null];
|
|
38
|
+
}
|
|
39
|
+
};
|
|
@@ -4,8 +4,9 @@ antora:
|
|
|
4
4
|
- require: ./extensions/setLatestVersion.js
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
const GetLatestRedpandaVersion = require('./
|
|
8
|
-
const GetLatestConsoleVersion = require('./
|
|
7
|
+
const GetLatestRedpandaVersion = require('./get-latest-redpanda-version');
|
|
8
|
+
const GetLatestConsoleVersion = require('./get-latest-console-version');
|
|
9
|
+
const GetLatestOperatorVersion = require('./get-latest-operator-version');
|
|
9
10
|
const chalk = require('chalk')
|
|
10
11
|
|
|
11
12
|
|
|
@@ -15,25 +16,28 @@ module.exports.register = function ({ config }) {
|
|
|
15
16
|
logger.warn('REDPANDA_GITHUB_TOKEN environment variable not set. Attempting unauthenticated request.');
|
|
16
17
|
}
|
|
17
18
|
this
|
|
18
|
-
.on('contentClassified', async ({
|
|
19
|
+
.on('contentClassified', async ({ contentCatalog }) => {
|
|
19
20
|
try {
|
|
20
21
|
const LatestRedpandaVersion = await GetLatestRedpandaVersion();
|
|
21
22
|
const LatestConsoleVersion = await GetLatestConsoleVersion();
|
|
23
|
+
const LatestOperatorVersion = await GetLatestOperatorVersion();
|
|
22
24
|
if (LatestRedpandaVersion.length !== 2 || !LatestRedpandaVersion[0]) {
|
|
23
25
|
logger.warn('Failed to get the latest Redpanda version - using defaults');
|
|
24
26
|
}
|
|
25
27
|
if (!LatestConsoleVersion) {
|
|
26
28
|
logger.warn(`Failed to get latest Console version from GitHub - using default`)
|
|
27
29
|
}
|
|
30
|
+
if (!LatestOperatorVersion) {
|
|
31
|
+
logger.warn(`Failed to get latest Operator version from GitHub - using default`)
|
|
32
|
+
}
|
|
28
33
|
const components = await contentCatalog.getComponents();
|
|
29
34
|
for (let i = 0; i < components.length; i++) {
|
|
30
35
|
let component = components[i];
|
|
31
|
-
if (component.name !== 'ROOT' && component.name !== 'preview') continue
|
|
32
36
|
|
|
33
37
|
component.versions.forEach(({name, version, asciidoc}) => {
|
|
34
38
|
if (LatestConsoleVersion) {
|
|
35
39
|
asciidoc.attributes['latest-console-version'] = `${LatestConsoleVersion}`;
|
|
36
|
-
|
|
40
|
+
logger.info(`Set Redpanda Console version to')} ${LatestConsoleVersion} in ${name} ${version}`);
|
|
37
41
|
}
|
|
38
42
|
})
|
|
39
43
|
|
|
@@ -49,8 +53,16 @@ module.exports.register = function ({ config }) {
|
|
|
49
53
|
|
|
50
54
|
component.latest.asciidoc.attributes['full-version'] = `${LatestRedpandaVersion[0]}`;
|
|
51
55
|
component.latest.asciidoc.attributes['latest-release-commit'] = `${LatestRedpandaVersion[1]}`;
|
|
52
|
-
|
|
56
|
+
logger.info(`Set the latest Redpanda version to ${LatestRedpandaVersion[0]} ${LatestRedpandaVersion[1]}`)
|
|
57
|
+
|
|
58
|
+
if (!LatestOperatorVersion) continue;
|
|
59
|
+
|
|
60
|
+
component.latest.asciidoc.attributes['latest-operator-version'] = `${LatestOperatorVersion}`;
|
|
61
|
+
logger.info(`Set the latest Redpanda Operator version to ${LatestOperatorVersion}`)
|
|
53
62
|
}
|
|
63
|
+
console.log(`${chalk.green('Set Redpanda Console version to')} ${chalk.bold(LatestConsoleVersion)}`);
|
|
64
|
+
console.log(`${chalk.green('Set the latest Redpanda version to')} ${chalk.bold(LatestRedpandaVersion[0])} ${chalk.bold(LatestRedpandaVersion[1])}`)
|
|
65
|
+
console.log(`${chalk.green('Set the latest Redpanda Operator version to')} ${chalk.bold(LatestOperatorVersion)}`)
|
|
54
66
|
} catch(error) {
|
|
55
67
|
logger.warn(error)
|
|
56
68
|
}
|
package/package.json
CHANGED
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
// Fetch the latest release version from GitHub
|
|
2
|
-
const { Octokit } = require("@octokit/rest");
|
|
3
|
-
const { retry } = require("@octokit/plugin-retry");
|
|
4
|
-
const OctokitWithRetries = Octokit.plugin(retry);
|
|
5
|
-
const owner = 'redpanda-data';
|
|
6
|
-
const repo = 'redpanda';
|
|
7
|
-
|
|
8
|
-
let githubOptions = {
|
|
9
|
-
userAgent: 'Redpanda Docs',
|
|
10
|
-
baseUrl: 'https://api.github.com',
|
|
11
|
-
};
|
|
12
|
-
|
|
13
|
-
if (process.env.REDPANDA_GITHUB_TOKEN) {
|
|
14
|
-
githubOptions.auth = process.env.REDPANDA_GITHUB_TOKEN;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
const github = new OctokitWithRetries(githubOptions);
|
|
18
|
-
|
|
19
|
-
var latestRedpandaReleaseVersion;
|
|
20
|
-
var latestRedpandaReleaseCommitHash;
|
|
21
|
-
|
|
22
|
-
module.exports = async () => {
|
|
23
|
-
await github.rest.repos.getLatestRelease({
|
|
24
|
-
owner,
|
|
25
|
-
repo,
|
|
26
|
-
}).then(async function(release) {
|
|
27
|
-
const tag = release.data.tag_name;
|
|
28
|
-
latestRedpandaReleaseVersion = tag.replace('v','');
|
|
29
|
-
await github.rest.git.getRef({
|
|
30
|
-
owner,
|
|
31
|
-
repo,
|
|
32
|
-
ref: `/tags/${tag}`,
|
|
33
|
-
}).then(async function(tagRef) {
|
|
34
|
-
const releaseSha = tagRef.data.object.sha;
|
|
35
|
-
await github.rest.git.getTag({
|
|
36
|
-
owner,
|
|
37
|
-
repo,
|
|
38
|
-
tag_sha: releaseSha,
|
|
39
|
-
}).then((tag => latestRedpandaReleaseCommitHash = tag.data.object.sha.substring(0, 7)))
|
|
40
|
-
})
|
|
41
|
-
}).catch((error => {
|
|
42
|
-
console.error(error)
|
|
43
|
-
return null
|
|
44
|
-
}))
|
|
45
|
-
return [latestRedpandaReleaseVersion, latestRedpandaReleaseCommitHash];
|
|
46
|
-
};
|