@redpanda-data/docs-extensions-and-macros 3.9.0 → 3.10.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 +90 -0
- package/extensions/add-global-attributes.js +2 -3
- package/extensions/add-pages-to-root.js +30 -0
- package/extensions/aggregate-terms.js +3 -4
- package/extensions/algolia-indexer/generate-index.js +1 -1
- package/extensions/algolia-indexer/index.js +4 -7
- package/extensions/collect-bloblang-samples.js +78 -0
- package/extensions/generate-rp-connect-categories.js +1 -1
- package/extensions/generate-rp-connect-info.js +1 -1
- package/extensions/validate-attributes.js +2 -2
- package/extensions/version-fetcher/set-latest-version.js +1 -2
- package/package.json +3 -1
package/README.adoc
CHANGED
|
@@ -46,6 +46,96 @@ This section documents the Antora extensions provided by this library and how to
|
|
|
46
46
|
|
|
47
47
|
IMPORTANT: Ensure you register each extension under the `antora.extensions` key in the playbook, not the `asciidoc.extensions` key.
|
|
48
48
|
|
|
49
|
+
=== Add Bloblang samples to pages
|
|
50
|
+
|
|
51
|
+
The `collect-bloblang-samples` extension processes Bloblang examples from YAML files in the `examples` directory of the `redpanda-connect` component. This extension ensures that these examples are accessible as structured data for use in UI components or documentation, such as sample dropdowns in a Bloblang playground.
|
|
52
|
+
|
|
53
|
+
It validates, sorts, and attaches the processed examples as a JSON object to the Antora page attributes. The extension ensures examples have unique titles, mandatory fields (`input` and `mapping`), and are sorted in alphabetical order.
|
|
54
|
+
|
|
55
|
+
==== Environment variables
|
|
56
|
+
|
|
57
|
+
This extension does not require any environment variables.
|
|
58
|
+
|
|
59
|
+
==== Configuration options
|
|
60
|
+
|
|
61
|
+
To enable the extension, add it to your Antora playbook under the `antora.extensions` key. No additional configuration is required.
|
|
62
|
+
|
|
63
|
+
[,yaml]
|
|
64
|
+
----
|
|
65
|
+
antora:
|
|
66
|
+
extensions:
|
|
67
|
+
- require: '@redpanda-data/docs-extensions-and-macros/extensions/collect-bloblang-samples'
|
|
68
|
+
----
|
|
69
|
+
|
|
70
|
+
==== Example Bloblang YAML file
|
|
71
|
+
|
|
72
|
+
The following YAML file is an example of how to define a Bloblang sample:
|
|
73
|
+
|
|
74
|
+
[,yaml]
|
|
75
|
+
----
|
|
76
|
+
title: Hello world
|
|
77
|
+
input: |
|
|
78
|
+
{
|
|
79
|
+
"message": "hello world"
|
|
80
|
+
}
|
|
81
|
+
mapping: |
|
|
82
|
+
root.message = this.message.uppercase()
|
|
83
|
+
----
|
|
84
|
+
|
|
85
|
+
==== Sample output
|
|
86
|
+
|
|
87
|
+
The processed examples are added as JSON to the `page-bloblang-samples` attribute. For example:
|
|
88
|
+
|
|
89
|
+
[,json]
|
|
90
|
+
----
|
|
91
|
+
{
|
|
92
|
+
"hello-world.yaml": {
|
|
93
|
+
"title": "Hello world",
|
|
94
|
+
"input": "{\n \"message\": \"hello world\"\n}\n",
|
|
95
|
+
"mapping": "root.message = this.message.uppercase()\n"
|
|
96
|
+
},
|
|
97
|
+
"array-processing.yaml": {
|
|
98
|
+
"title": "Array processing",
|
|
99
|
+
"input": "{\n \"numbers\": [1, 2, 3, 4, 5]\n}\n",
|
|
100
|
+
"mapping": "root.even_numbers = this.numbers.filter(n -> n % 2 == 0)"
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
----
|
|
104
|
+
|
|
105
|
+
=== Add pages to root
|
|
106
|
+
|
|
107
|
+
The `add-pages-to-root` extension allows you to copy files from your Antora content catalog to the root of the site during the build process. This is particularly useful for files like `llms.txt` or any custom files that need to be directly accessible at the site's root level.
|
|
108
|
+
|
|
109
|
+
This extension processes a list of file paths provided in the playbook configuration, locates those files in the Antora content catalog, and adds them to the site's root directory during the publishing phase. Each file's content and basename are preserved in the process.
|
|
110
|
+
|
|
111
|
+
==== Environment variables
|
|
112
|
+
|
|
113
|
+
This extension does not require any environment variables.
|
|
114
|
+
|
|
115
|
+
==== Configuration options
|
|
116
|
+
|
|
117
|
+
Add the `add-pages-to-root` extension to your Antora playbook under the `antora.extensions` key, and specify the list of files to process in the `files` configuration.
|
|
118
|
+
|
|
119
|
+
[source,yaml]
|
|
120
|
+
----
|
|
121
|
+
antora:
|
|
122
|
+
extensions:
|
|
123
|
+
- require: '@redpanda-data/docs-extensions-and-macros/extensions/add-pages-to-root'
|
|
124
|
+
files:
|
|
125
|
+
- home:ROOT:attachment$custom-file.txt
|
|
126
|
+
----
|
|
127
|
+
|
|
128
|
+
==== Registration example
|
|
129
|
+
|
|
130
|
+
[source,yaml]
|
|
131
|
+
----
|
|
132
|
+
antora:
|
|
133
|
+
extensions:
|
|
134
|
+
- require: '@redpanda-data/docs-extensions-and-macros/extensions/add-pages-to-root'
|
|
135
|
+
files:
|
|
136
|
+
- home:ROOT:attachment$custom-file.txt
|
|
137
|
+
----
|
|
138
|
+
|
|
49
139
|
=== Algolia indexer
|
|
50
140
|
|
|
51
141
|
This extension generates an Algolia index for each version of each component. The index entries are then saved to Algolia using the `saveObjects()` method, and also saved as JSON files in the site catalog. JSON files are published to the site root using the template `algolia-<component>-<version>.json`.
|
|
@@ -13,7 +13,6 @@ const ATTRIBUTES_PATH = 'modules/ROOT/partials/'; // Default path within the 's
|
|
|
13
13
|
|
|
14
14
|
module.exports.register = function ({ config }) {
|
|
15
15
|
const logger = this.getLogger('global-attributes-extension');
|
|
16
|
-
const chalk = require('chalk');
|
|
17
16
|
|
|
18
17
|
/**
|
|
19
18
|
* Load global attributes from a specified local file if provided.
|
|
@@ -30,7 +29,7 @@ module.exports.register = function ({ config }) {
|
|
|
30
29
|
const fileAttributes = yaml.load(fileContents);
|
|
31
30
|
|
|
32
31
|
siteCatalog.attributeFile = _.merge({}, fileAttributes);
|
|
33
|
-
|
|
32
|
+
logger.info(`Loaded global attributes from local file "${localAttributesFile}".`);
|
|
34
33
|
return true; // Return true if the local attributes were successfully loaded
|
|
35
34
|
|
|
36
35
|
} catch (error) {
|
|
@@ -64,7 +63,7 @@ module.exports.register = function ({ config }) {
|
|
|
64
63
|
const fileAttributes = yaml.load(file.contents.toString('utf8'));
|
|
65
64
|
return _.merge(acc, fileAttributes);
|
|
66
65
|
}, {});
|
|
67
|
-
|
|
66
|
+
logger.info('Loaded global attributes from shared component.');
|
|
68
67
|
}
|
|
69
68
|
break;
|
|
70
69
|
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
module.exports.register = function ({ config }) {
|
|
2
|
+
this.on('beforePublish', ({ siteCatalog, contentCatalog }) => {
|
|
3
|
+
const logger = this.getLogger('add-pages-to-site-root');
|
|
4
|
+
if (!config || !config.files || !config.files.length) {
|
|
5
|
+
logger.debug('No files configured to be added to the root directory.');
|
|
6
|
+
return;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
logger.debug('Files to process:', config.files);
|
|
10
|
+
|
|
11
|
+
config.files.forEach(filePath => {
|
|
12
|
+
const resource = contentCatalog.resolveResource(filePath);
|
|
13
|
+
if (resource) {
|
|
14
|
+
const basename = resource.src.basename; // Get the file's basename
|
|
15
|
+
const contentsBuffer = resource.contents; // Access the file's contents as a Buffer
|
|
16
|
+
logger.debug(`Processing file: ${basename}`);
|
|
17
|
+
// Add the file to the root directory in the site catalog
|
|
18
|
+
siteCatalog.addFile({
|
|
19
|
+
contents: contentsBuffer,
|
|
20
|
+
out: {
|
|
21
|
+
path: basename, // Add the file to the root directory
|
|
22
|
+
},
|
|
23
|
+
});
|
|
24
|
+
delete resource.out
|
|
25
|
+
} else {
|
|
26
|
+
logger.warn(`File not resolved: ${filePath}`);
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
};
|
|
@@ -11,7 +11,6 @@ const TERMS_PATH = 'modules/terms/partials/'; // Default path within the 'share
|
|
|
11
11
|
|
|
12
12
|
module.exports.register = function ({ config }) {
|
|
13
13
|
const logger = this.getLogger('term-aggregation-extension');
|
|
14
|
-
const chalk = require('chalk');
|
|
15
14
|
|
|
16
15
|
/**
|
|
17
16
|
* Function to process term content, extracting hover text and links.
|
|
@@ -67,7 +66,7 @@ module.exports.register = function ({ config }) {
|
|
|
67
66
|
siteCatalog.termsByCategory[formattedCategory].push({ name: file, content: termContent });
|
|
68
67
|
});
|
|
69
68
|
|
|
70
|
-
|
|
69
|
+
logger.info(`Categorized terms from local terms path "${termsPath}".`);
|
|
71
70
|
return true;
|
|
72
71
|
|
|
73
72
|
} catch (error) {
|
|
@@ -109,7 +108,7 @@ module.exports.register = function ({ config }) {
|
|
|
109
108
|
siteCatalog.termsByCategory[formattedCategory].push({ name: file.basename, content: termContent });
|
|
110
109
|
});
|
|
111
110
|
|
|
112
|
-
|
|
111
|
+
logger.info('Categorized terms from shared component.');
|
|
113
112
|
break;
|
|
114
113
|
}
|
|
115
114
|
}
|
|
@@ -150,7 +149,7 @@ module.exports.register = function ({ config }) {
|
|
|
150
149
|
});
|
|
151
150
|
|
|
152
151
|
glossaryPage.contents = Buffer.from(glossaryContent, 'utf8');
|
|
153
|
-
|
|
152
|
+
logger.info(`Merged terms into glossary for ${component} component${version ? ' version ' + version : ''}.`);
|
|
154
153
|
} else {
|
|
155
154
|
logger.info(`Skipping ${title} ${version ? ' version ' + version : ''} - No glossary page (reference:glossary.adoc) found`);
|
|
156
155
|
}
|
|
@@ -104,7 +104,7 @@ function generateIndex (playbook, contentCatalog, { indexLatestOnly = false, exc
|
|
|
104
104
|
// Start handling the article content
|
|
105
105
|
const article = root.querySelector('article.doc')
|
|
106
106
|
if (!article) {
|
|
107
|
-
logger.
|
|
107
|
+
logger.warn(`Page is not an article...skipping ${page.pub.url}`)
|
|
108
108
|
continue
|
|
109
109
|
}
|
|
110
110
|
|
|
@@ -1,12 +1,9 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
3
|
const generateIndex = require('./generate-index')
|
|
4
|
-
const chalk = require('chalk')
|
|
5
4
|
const algoliasearch = require('algoliasearch')
|
|
6
5
|
const http = require('http')
|
|
7
6
|
const https = require('https')
|
|
8
|
-
const fs = require('fs')
|
|
9
|
-
const path = require('path')
|
|
10
7
|
const _ = require('lodash')
|
|
11
8
|
process.env.UV_THREADPOOL_SIZE=16
|
|
12
9
|
|
|
@@ -106,7 +103,7 @@ function register({
|
|
|
106
103
|
// Upload new records only if the objects have been updated or they are new.
|
|
107
104
|
// See https://www.algolia.com/doc/api-reference/api-methods/batch/?client=javascript
|
|
108
105
|
await client.multipleBatch(batchActions).then(() => {
|
|
109
|
-
|
|
106
|
+
logger.info('Batch operations completed successfully');
|
|
110
107
|
}).catch(error => {
|
|
111
108
|
logger.error(`Error uploading records to Algolia: ${error.message}`);
|
|
112
109
|
});
|
|
@@ -127,10 +124,10 @@ function register({
|
|
|
127
124
|
});
|
|
128
125
|
}
|
|
129
126
|
|
|
130
|
-
|
|
131
|
-
|
|
127
|
+
logger.info('Updated records:' + totalObjectsToUpdate)
|
|
128
|
+
logger.info('New records:' + totalObjectsToAdd)
|
|
132
129
|
|
|
133
|
-
totalObjectsToAdd === 0 && totalObjectsToUpdate === 0 &&
|
|
130
|
+
totalObjectsToAdd === 0 && totalObjectsToUpdate === 0 && logger.info('No new records uploaded or existing records updated')
|
|
134
131
|
})
|
|
135
132
|
|
|
136
133
|
process.on('exit', () => {
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
const yaml = require('js-yaml');
|
|
2
|
+
|
|
3
|
+
module.exports.register = function () {
|
|
4
|
+
const logger = this.getLogger('collect-bloblang-samples');
|
|
5
|
+
|
|
6
|
+
this.on('contentClassified', ({ contentCatalog }) => {
|
|
7
|
+
const collectExamples = (examples, componentName) => {
|
|
8
|
+
const bloblangSamples = [];
|
|
9
|
+
const seenTitles = new Set();
|
|
10
|
+
|
|
11
|
+
examples
|
|
12
|
+
.filter((example) => example.src.relative.startsWith('playground/')) // Only include files in the 'bloblang' subdirectory
|
|
13
|
+
.forEach((example) => {
|
|
14
|
+
try {
|
|
15
|
+
const content = example.contents.toString('utf8');
|
|
16
|
+
const parsedContent = yaml.load(content);
|
|
17
|
+
|
|
18
|
+
if (!parsedContent.title) {
|
|
19
|
+
logger.warn(`Skipping example '${example.src.basename}' in '${componentName}': Missing title.`);
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
if (seenTitles.has(parsedContent.title)) {
|
|
24
|
+
logger.warn(
|
|
25
|
+
`Duplicate title found: '${parsedContent.title}' in '${example.src.basename}' (${componentName}). Skipping.`
|
|
26
|
+
);
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
if (!parsedContent.input || !parsedContent.mapping) {
|
|
31
|
+
logger.warn(
|
|
32
|
+
`Skipping example '${example.src.basename}' in '${componentName}': Missing input or mapping.`
|
|
33
|
+
);
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
logger.info(`Loaded example: ${example.src.basename} with title: '${parsedContent.title}'`);
|
|
38
|
+
seenTitles.add(parsedContent.title);
|
|
39
|
+
|
|
40
|
+
bloblangSamples.push({ filename: example.src.basename, ...parsedContent });
|
|
41
|
+
} catch (error) {
|
|
42
|
+
logger.error(`Error processing example '${example.src.basename}' in '${componentName}':`, error);
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
bloblangSamples.sort((a, b) => a.title.localeCompare(b.title));
|
|
47
|
+
|
|
48
|
+
return bloblangSamples.reduce((acc, sample) => {
|
|
49
|
+
acc[sample.filename] = sample;
|
|
50
|
+
return acc;
|
|
51
|
+
}, {});
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
// Fetch examples from both components
|
|
55
|
+
const examples = contentCatalog.findBy({ component: 'redpanda-connect', family: 'example' });
|
|
56
|
+
const previewExamples = contentCatalog.findBy({ component: 'preview', family: 'example' });
|
|
57
|
+
|
|
58
|
+
if (!examples.length) logger.warn(`No examples found in the 'redpanda-connect' component.`);
|
|
59
|
+
|
|
60
|
+
// Get components
|
|
61
|
+
const connect = contentCatalog.getComponents().find((c) => c.name === 'redpanda-connect');
|
|
62
|
+
const preview = contentCatalog.getComponents().find((c) => c.name === 'preview');
|
|
63
|
+
|
|
64
|
+
if (connect) {
|
|
65
|
+
const connectSamples = collectExamples(examples, 'redpanda-connect');
|
|
66
|
+
connect.latest.asciidoc.attributes['page-bloblang-samples'] = JSON.stringify(connectSamples);
|
|
67
|
+
logger.debug(`Bloblang samples added to 'redpanda-connect': ${JSON.stringify(connectSamples, null, 2)}`);
|
|
68
|
+
} else {
|
|
69
|
+
logger.warn(`Component 'redpanda-connect' not found.`);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (preview) {
|
|
73
|
+
const previewSamples = collectExamples(previewExamples, 'preview');
|
|
74
|
+
preview.latest.asciidoc.attributes['page-bloblang-samples'] = JSON.stringify(previewSamples);
|
|
75
|
+
logger.debug(`Bloblang samples added to 'preview': ${JSON.stringify(previewSamples, null, 2)}`);
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
};
|
|
@@ -116,7 +116,7 @@ module.exports.register = function ({ config }) {
|
|
|
116
116
|
redpandaConnect.latest.asciidoc.attributes.driverSupportData = driverSupportData
|
|
117
117
|
redpandaConnect.latest.asciidoc.attributes.cacheSupportData = cacheSupportData
|
|
118
118
|
|
|
119
|
-
logger.
|
|
119
|
+
logger.debug(`Added Redpanda Connect data to latest Asciidoc object.`)
|
|
120
120
|
logger.debug(`${JSON.stringify({ connectCategoriesData, flatComponentsData }, null, 2)}`)
|
|
121
121
|
} catch (error) {
|
|
122
122
|
logger.error(`Error processing Redpanda Connect files: ${error.message}`)
|
|
@@ -141,7 +141,7 @@ module.exports.register = function ({ config }) {
|
|
|
141
141
|
|
|
142
142
|
// Log a warning if neither URL was found (only warn for missing cloud if it should support cloud)
|
|
143
143
|
if (!redpandaConnectUrl && (!redpandaCloudUrl && is_cloud_supported === 'y')) {
|
|
144
|
-
logger.
|
|
144
|
+
logger.warn(`Docs missing for: ${connector} of type: ${type}`);
|
|
145
145
|
}
|
|
146
146
|
|
|
147
147
|
// Return the translated and enriched row
|
|
@@ -34,7 +34,7 @@ function processEnvironmentAttributes(page, logger) {
|
|
|
34
34
|
// If the env attribute exists, set a corresponding page- attribute for use in the UI
|
|
35
35
|
const pageEnvAttr = `page-${envAttr}`;
|
|
36
36
|
page.asciidoc.attributes[pageEnvAttr] = true;
|
|
37
|
-
logger.
|
|
37
|
+
logger.debug(`Set '${pageEnvAttr}' for ${page.asciidoc.attributes['page-relative-src-path']}`);
|
|
38
38
|
}
|
|
39
39
|
});
|
|
40
40
|
}
|
|
@@ -53,7 +53,7 @@ function validateCategories(pageCategoryList, pageInfo, categoryMap, logger) {
|
|
|
53
53
|
if (!adjustedCategories.has(parentCategory)) {
|
|
54
54
|
// Add the parent category since it's missing
|
|
55
55
|
adjustedCategories.add(parentCategory);
|
|
56
|
-
logger.
|
|
56
|
+
logger.debug(`Added missing parent category '${parentCategory}' for subcategory '${category}' in ${pageInfo}`);
|
|
57
57
|
}
|
|
58
58
|
}
|
|
59
59
|
// Check if the current category is not a valid category or subcategory
|
|
@@ -6,7 +6,6 @@ module.exports.register = function ({ config }) {
|
|
|
6
6
|
const GetLatestOperatorVersion = require('./get-latest-operator-version');
|
|
7
7
|
const GetLatestHelmChartVersion = require('./get-latest-redpanda-helm-version');
|
|
8
8
|
const GetLatestConnectVersion = require('./get-latest-connect');
|
|
9
|
-
const chalk = require('chalk');
|
|
10
9
|
const logger = this.getLogger('set-latest-version-extension');
|
|
11
10
|
|
|
12
11
|
if (!process.env.REDPANDA_GITHUB_TOKEN) {
|
|
@@ -96,7 +95,7 @@ module.exports.register = function ({ config }) {
|
|
|
96
95
|
}
|
|
97
96
|
});
|
|
98
97
|
|
|
99
|
-
|
|
98
|
+
logger.info('Updated Redpanda documentation versions successfully.');
|
|
100
99
|
logger.info(`Latest Redpanda version: ${latestVersions.redpanda.latestRedpandaRelease.version}`);
|
|
101
100
|
if (latestVersions.redpanda.latestRCRelease) logger.info(`Latest Redpanda beta version: ${latestVersions.redpanda.latestRCRelease.version}`);
|
|
102
101
|
logger.info(`Latest Connect version: ${latestVersions.connect}`);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@redpanda-data/docs-extensions-and-macros",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.10.0",
|
|
4
4
|
"description": "Antora extensions and macros developed for Redpanda documentation.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"antora",
|
|
@@ -30,6 +30,8 @@
|
|
|
30
30
|
"require": "./extensions/unlisted-pages.js"
|
|
31
31
|
},
|
|
32
32
|
"./extensions/replace-attributes-in-attachments": "./extensions/replace-attributes-in-attachments.js",
|
|
33
|
+
"./extensions/add-pages-to-root": "./extensions/add-pages-to-root.js",
|
|
34
|
+
"./extensions/collect-bloblang-samples": "./extensions/collect-bloblang-samples.js",
|
|
33
35
|
"./extensions/generate-rp-connect-categories": "./extensions/generate-rp-connect-categories.js",
|
|
34
36
|
"./extensions/generate-rp-connect-info": "./extensions/generate-rp-connect-info.js",
|
|
35
37
|
"./extensions/add-global-attributes": "./extensions/add-global-attributes.js",
|