@redpanda-data/docs-extensions-and-macros 3.4.0 → 3.4.1
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/extensions/find-related-docs.js +52 -33
- package/extensions/unlisted-pages.js +67 -41
- package/extensions/unpublish-pages.js +32 -7
- package/extensions/version-fetcher/get-latest-redpanda-version.js +27 -6
- package/extensions/version-fetcher/set-latest-version.js +10 -5
- package/package.json +1 -1
|
@@ -4,67 +4,86 @@ module.exports.register = function ({ config }) {
|
|
|
4
4
|
const logger = this.getLogger('related-docs-extension');
|
|
5
5
|
|
|
6
6
|
this.on('documentsConverted', async ({ contentCatalog, siteCatalog }) => {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
7
|
+
// Find the latest version of each component
|
|
8
|
+
const latestVersions = {};
|
|
9
|
+
contentCatalog.getComponents().forEach(component => {
|
|
10
|
+
latestVersions[component.name] = component.latest.version;
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
// Retrieve all documents and labs from the latest versions of each component
|
|
14
|
+
const allDocs = [];
|
|
15
|
+
const allLabs = contentCatalog.findBy({ component: 'redpanda-labs', family: 'page', version: latestVersions['redpanda-labs'] });
|
|
16
|
+
|
|
17
|
+
Object.keys(latestVersions).forEach(component => {
|
|
18
|
+
if (component === 'redpanda-labs') return;
|
|
19
|
+
allDocs.push(...contentCatalog.findBy({ component, family: 'page', version: latestVersions[component] }));
|
|
20
|
+
});
|
|
21
|
+
allLabs.forEach((labPage) => {
|
|
22
|
+
const relatedDocs = new Set();
|
|
23
|
+
const relatedLabs = new Set();
|
|
24
|
+
const sourceAttributes = labPage.asciidoc.attributes;
|
|
12
25
|
const pageCategories = sourceAttributes['page-categories'];
|
|
13
26
|
if (!pageCategories) return;
|
|
14
27
|
const sourceCategoryList = pageCategories.split(',').map(c => c.trim());
|
|
15
|
-
const sourceDeploymentType = getDeploymentType(sourceAttributes)
|
|
16
|
-
const docs = contentCatalog.findBy({ component: 'ROOT', family: 'page' });
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
related
|
|
20
|
-
|
|
21
|
-
|
|
28
|
+
const sourceDeploymentType = getDeploymentType(sourceAttributes);
|
|
29
|
+
const docs = contentCatalog.findBy({ component: 'ROOT', family: 'page', version: latestVersions['ROOT'] });
|
|
30
|
+
|
|
31
|
+
allDocs.forEach((docPage) => {
|
|
32
|
+
const related = findRelated(docPage, sourceCategoryList, sourceDeploymentType, logger);
|
|
33
|
+
if (related) relatedDocs.add(JSON.stringify(related));
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
allLabs.forEach((targetLabPage) => {
|
|
22
37
|
if (targetLabPage === labPage) return;
|
|
23
38
|
|
|
24
39
|
const related = findRelated(targetLabPage, sourceCategoryList, sourceDeploymentType, logger);
|
|
25
|
-
if (related) relatedLabs.
|
|
40
|
+
if (related) relatedLabs.add(JSON.stringify(related));
|
|
26
41
|
});
|
|
27
42
|
|
|
43
|
+
// Convert Sets back to arrays and remove duplicates
|
|
44
|
+
const uniqueRelatedDocs = Array.from(relatedDocs).map(item => JSON.parse(item));
|
|
45
|
+
const uniqueRelatedLabs = Array.from(relatedLabs).map(item => JSON.parse(item));
|
|
46
|
+
|
|
28
47
|
// Store related docs and labs in the lab page attributes
|
|
29
|
-
if (
|
|
30
|
-
labPage.asciidoc.attributes['page-related-docs'] = JSON.stringify(
|
|
48
|
+
if (uniqueRelatedDocs.length > 0) {
|
|
49
|
+
labPage.asciidoc.attributes['page-related-docs'] = JSON.stringify(uniqueRelatedDocs);
|
|
31
50
|
}
|
|
32
51
|
|
|
33
|
-
if (
|
|
34
|
-
labPage.asciidoc.attributes['page-related-labs'] = JSON.stringify(
|
|
52
|
+
if (uniqueRelatedLabs.length > 0) {
|
|
53
|
+
labPage.asciidoc.attributes['page-related-labs'] = JSON.stringify(uniqueRelatedLabs);
|
|
35
54
|
}
|
|
36
55
|
|
|
37
|
-
if (
|
|
56
|
+
if (uniqueRelatedDocs.length > 0 || uniqueRelatedLabs.length > 0) {
|
|
38
57
|
logger.info(`Set related docs and labs attributes for ${labPage.asciidoc.doctitle}`);
|
|
39
58
|
}
|
|
40
|
-
})
|
|
41
|
-
})
|
|
42
|
-
}
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
};
|
|
43
62
|
|
|
44
63
|
function findRelated(docPage, sourceCategoryList, sourceDeploymentType, logger) {
|
|
45
|
-
const targetAttributes = docPage.asciidoc.attributes
|
|
64
|
+
const targetAttributes = docPage.asciidoc.attributes;
|
|
46
65
|
const pageCategories = targetAttributes['page-categories'];
|
|
47
66
|
if (!pageCategories) return null;
|
|
48
67
|
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)) {
|
|
68
|
+
const targetDeploymentType = getDeploymentType(targetAttributes);
|
|
69
|
+
const categoryMatch = hasMatchingCategory(sourceCategoryList, targetCategoryList);
|
|
70
|
+
if (categoryMatch && (!targetDeploymentType || sourceDeploymentType === targetDeploymentType)) {
|
|
52
71
|
return {
|
|
53
72
|
title: docPage.asciidoc.doctitle,
|
|
54
73
|
url: docPage.pub.url,
|
|
55
|
-
}
|
|
74
|
+
};
|
|
56
75
|
}
|
|
57
|
-
return null
|
|
76
|
+
return null;
|
|
58
77
|
}
|
|
59
78
|
|
|
60
|
-
function getDeploymentType
|
|
79
|
+
function getDeploymentType(attributes) {
|
|
61
80
|
return attributes['env-kubernetes'] ? 'Kubernetes'
|
|
62
81
|
: attributes['env-linux'] ? 'Linux'
|
|
63
82
|
: attributes['env-docker'] ? 'Docker'
|
|
64
|
-
: attributes.cloud ? 'Redpanda Cloud'
|
|
65
|
-
: ''
|
|
83
|
+
: attributes.cloud ? 'Redpanda Cloud'
|
|
84
|
+
: '';
|
|
66
85
|
}
|
|
67
86
|
|
|
68
|
-
function hasMatchingCategory
|
|
69
|
-
return sourcePageCategories.every(
|
|
70
|
-
}
|
|
87
|
+
function hasMatchingCategory(sourcePageCategories, targetPageCategories) {
|
|
88
|
+
return sourcePageCategories.every(category => targetPageCategories.includes(category));
|
|
89
|
+
}
|
|
@@ -1,54 +1,68 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
1
3
|
module.exports.register = function ({ config }) {
|
|
2
|
-
const { addToNavigation, unlistedPagesHeading = 'Unlisted Pages' } = config
|
|
3
|
-
const logger = this.getLogger('unlisted-pages-extension')
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
} else if (unlistedPages.length && addToNavigation) {
|
|
23
|
-
nav.push({
|
|
24
|
-
content: unlistedPagesHeading,
|
|
25
|
-
items: unlistedPages.map((page) => {
|
|
26
|
-
return { content: page.asciidoc.navtitle, url: page.pub.url, urlType: 'internal' }
|
|
27
|
-
}),
|
|
28
|
-
root: true,
|
|
29
|
-
})
|
|
30
|
-
}
|
|
31
|
-
})
|
|
32
|
-
})
|
|
33
|
-
})
|
|
34
|
-
}
|
|
4
|
+
const { addToNavigation, unlistedPagesHeading = 'Unlisted Pages' } = config;
|
|
5
|
+
const logger = this.getLogger('unlisted-pages-extension');
|
|
6
|
+
|
|
7
|
+
this.on('navigationBuilt', ({ siteCatalog, contentCatalog }) => {
|
|
8
|
+
contentCatalog.getComponents().forEach(({ versions }) => {
|
|
9
|
+
versions.forEach(({ name: component, version, navigation: nav, url: defaultUrl }) => {
|
|
10
|
+
if (component === 'api') return;
|
|
11
|
+
if (!nav) return;
|
|
12
|
+
const currentComponent = contentCatalog.getComponent(component);
|
|
13
|
+
const prerelease = currentComponent && currentComponent.latestPrerelease ? currentComponent.latestPrerelease : false;
|
|
14
|
+
|
|
15
|
+
const navEntriesByUrl = getNavEntriesByUrl(nav);
|
|
16
|
+
const unlistedPages = contentCatalog
|
|
17
|
+
.findBy({ component, version, family: 'page' })
|
|
18
|
+
.reduce((collector, page) => {
|
|
19
|
+
if (siteCatalog.unpublishedPages.includes(page.pub.url)) {
|
|
20
|
+
logger.info({ file: page.src, source: page.src.origin }, 'removing unpublished page from nav tree');
|
|
21
|
+
removePageFromNav(nav, page.pub.url); // Remove the page from navigationCatalog
|
|
22
|
+
return collector; // Skip adding this page to the collector
|
|
23
|
+
}
|
|
35
24
|
|
|
36
|
-
|
|
25
|
+
if (!(page.pub.url in navEntriesByUrl) && page.pub.url !== defaultUrl) {
|
|
26
|
+
logger.warn({ file: page.src, source: page.src.origin }, 'detected unlisted page');
|
|
27
|
+
return collector.concat(page);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return collector;
|
|
31
|
+
}, []);
|
|
32
|
+
|
|
33
|
+
if (unlistedPages.length && component === 'redpanda-connect') {
|
|
34
|
+
// Some component pages for Redpanda Connect are autogenerated. This function tries to add unlisted component pages to the nav in case a new one gets created without updating the nav.
|
|
35
|
+
addRedpandaConnectPagesToNav(nav[0].items, unlistedPages);
|
|
36
|
+
} else if (unlistedPages.length && addToNavigation) {
|
|
37
|
+
nav.push({
|
|
38
|
+
content: unlistedPagesHeading,
|
|
39
|
+
items: unlistedPages.map((page) => {
|
|
40
|
+
return { content: page.asciidoc.navtitle, url: page.pub.url, urlType: 'internal' };
|
|
41
|
+
}),
|
|
42
|
+
root: true,
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
});
|
|
47
|
+
});
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
function getNavEntriesByUrl(items = [], accum = {}) {
|
|
37
51
|
items.forEach((item) => {
|
|
38
|
-
if (item.urlType === 'internal') accum[item.url.split('#')[0]] = item
|
|
39
|
-
getNavEntriesByUrl(item.items, accum)
|
|
40
|
-
})
|
|
41
|
-
return accum
|
|
52
|
+
if (item.urlType === 'internal') accum[item.url.split('#')[0]] = item;
|
|
53
|
+
getNavEntriesByUrl(item.items, accum);
|
|
54
|
+
});
|
|
55
|
+
return accum;
|
|
42
56
|
}
|
|
43
57
|
|
|
44
58
|
function addRedpandaConnectPagesToNav(navItems, pages) {
|
|
45
59
|
// get the Components nav section
|
|
46
60
|
let componentsSection = navItems.find(item => item.content === 'Components');
|
|
47
|
-
if (!componentsSection) return
|
|
61
|
+
if (!componentsSection) return;
|
|
48
62
|
|
|
49
63
|
pages.forEach(page => {
|
|
50
64
|
const dirname = page.out.dirname;
|
|
51
|
-
if (!dirname.includes('/components/')) return
|
|
65
|
+
if (!dirname.includes('/components/')) return;
|
|
52
66
|
const heading = page.asciidoc.attributes.doctitle;
|
|
53
67
|
const pathParts = dirname.split('/').slice(2); // Get the type
|
|
54
68
|
// get existing nav items inside the Components tree
|
|
@@ -66,4 +80,16 @@ function addRedpandaConnectPagesToNav(navItems, pages) {
|
|
|
66
80
|
|
|
67
81
|
currentLevel.push({ content: page.asciidoc.navtitle || page.src.stem, url: page.pub.url, urlType: 'internal' });
|
|
68
82
|
});
|
|
69
|
-
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function removePageFromNav(navItems, urlToRemove) {
|
|
86
|
+
// Remove the page from the navigation items
|
|
87
|
+
for (let i = navItems.length - 1; i >= 0; i--) {
|
|
88
|
+
const item = navItems[i];
|
|
89
|
+
if (item.url === urlToRemove) {
|
|
90
|
+
navItems.splice(i, 1);
|
|
91
|
+
} else if (item.items && item.items.length > 0) {
|
|
92
|
+
removePageFromNav(item.items, urlToRemove);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
@@ -1,9 +1,34 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
1
3
|
module.exports.register = function () {
|
|
2
|
-
this.on('documentsConverted', ({ contentCatalog }) => {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
this.on('documentsConverted', ({ siteCatalog, contentCatalog }) => {
|
|
5
|
+
// Get all pages that have an `out` property
|
|
6
|
+
const pages = contentCatalog.getPages((page) => page.out);
|
|
7
|
+
let componentName;
|
|
8
|
+
siteCatalog.unpublishedPages = []
|
|
9
|
+
|
|
10
|
+
// Iterate over each page
|
|
11
|
+
pages.forEach((page) => {
|
|
12
|
+
// Retrieve the component associated with the page
|
|
13
|
+
componentName = page.asciidoc?.attributes['page-component-name'];
|
|
14
|
+
const component = contentCatalog.getComponent(componentName);
|
|
15
|
+
|
|
16
|
+
// Check if the component has a latest prerelease
|
|
17
|
+
const prerelease = component && component.latestPrerelease ? component.latestPrerelease : false;
|
|
18
|
+
|
|
19
|
+
// Check the conditions for unpublishing the page
|
|
20
|
+
const shouldUnpublish = (
|
|
21
|
+
page.asciidoc?.attributes['page-unpublish'] ||
|
|
22
|
+
(page.asciidoc?.attributes['publish-only-during-beta']
|
|
23
|
+
&& !prerelease // No beta version available, meaning the beta period has ended
|
|
24
|
+
)
|
|
25
|
+
);
|
|
26
|
+
|
|
27
|
+
// If any condition is met, unpublish the page
|
|
28
|
+
if (shouldUnpublish) {
|
|
29
|
+
siteCatalog.unpublishedPages.push(page.pub.url)
|
|
30
|
+
delete page.out;
|
|
6
31
|
}
|
|
7
|
-
})
|
|
8
|
-
})
|
|
9
|
-
}
|
|
32
|
+
});
|
|
33
|
+
});
|
|
34
|
+
};
|
|
@@ -27,15 +27,17 @@ module.exports = async () => {
|
|
|
27
27
|
per_page: 50
|
|
28
28
|
});
|
|
29
29
|
|
|
30
|
-
// Filter valid semver tags and sort them to find the highest version
|
|
30
|
+
// Filter valid semver tags, exclude drafts, and sort them to find the highest version
|
|
31
31
|
const sortedReleases = releases.data
|
|
32
|
+
.filter(release => !release.draft)
|
|
32
33
|
.map(release => release.tag_name.replace(/^v/, ''))
|
|
33
|
-
.filter(tag => semver.valid(tag)
|
|
34
|
+
.filter(tag => semver.valid(tag))
|
|
34
35
|
// Sort in descending order to get the highest version first
|
|
35
36
|
.sort(semver.rcompare);
|
|
36
37
|
|
|
37
38
|
if (sortedReleases.length > 0) {
|
|
38
|
-
const latestRedpandaReleaseVersion = sortedReleases
|
|
39
|
+
const latestRedpandaReleaseVersion = sortedReleases.find(tag => !tag.includes('rc'));
|
|
40
|
+
const latestRcReleaseVersion = sortedReleases.find(tag => tag.includes('rc'));
|
|
39
41
|
|
|
40
42
|
// Get the commit hash for the highest version tag
|
|
41
43
|
const commitData = await github.rest.git.getRef({
|
|
@@ -45,13 +47,32 @@ module.exports = async () => {
|
|
|
45
47
|
});
|
|
46
48
|
const latestRedpandaReleaseCommitHash = commitData.data.object.sha;
|
|
47
49
|
|
|
48
|
-
|
|
50
|
+
let latestRcReleaseCommitHash = null;
|
|
51
|
+
if (latestRcReleaseVersion) {
|
|
52
|
+
const rcCommitData = await github.rest.git.getRef({
|
|
53
|
+
owner,
|
|
54
|
+
repo,
|
|
55
|
+
ref: `tags/v${latestRcReleaseVersion}`
|
|
56
|
+
});
|
|
57
|
+
latestRcReleaseCommitHash = rcCommitData.data.object.sha;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return {
|
|
61
|
+
latestRedpandaRelease: {
|
|
62
|
+
version: latestRedpandaReleaseVersion,
|
|
63
|
+
commitHash: latestRedpandaReleaseCommitHash.substring(0, 7)
|
|
64
|
+
},
|
|
65
|
+
latestRcRelease: latestRcReleaseVersion ? {
|
|
66
|
+
version: latestRcReleaseVersion,
|
|
67
|
+
commitHash: latestRcReleaseCommitHash.substring(0, 7)
|
|
68
|
+
} : null
|
|
69
|
+
};
|
|
49
70
|
} else {
|
|
50
71
|
console.log("No valid semver releases found for Redpanda.");
|
|
51
|
-
return
|
|
72
|
+
return { latestRedpandaRelease: null, latestRcRelease: null };
|
|
52
73
|
}
|
|
53
74
|
} catch (error) {
|
|
54
75
|
console.error('Failed to fetch Redpanda release information:', error);
|
|
55
|
-
return
|
|
76
|
+
return { latestRedpandaRelease: null, latestRcRelease: null };
|
|
56
77
|
}
|
|
57
78
|
};
|
|
@@ -39,18 +39,23 @@ module.exports.register = function ({ config }) {
|
|
|
39
39
|
asciidoc.attributes['latest-connect-version'] = `${LatestConnectVersion}@`
|
|
40
40
|
logger.info(`Set Redpanda Connect version to ${LatestConnectVersion} in ${name} ${version}`)
|
|
41
41
|
}
|
|
42
|
+
if (LatestRedpandaVersion && LatestRedpandaVersion.latestRcRelease) {
|
|
43
|
+
asciidoc.attributes['redpanda-beta-version'] = `${LatestRedpandaVersion.latestRcRelease.version}@`
|
|
44
|
+
asciidoc.attributes['redpanda-beta-commit'] = `${LatestRedpandaVersion.latestRcRelease.commitHash}@`
|
|
45
|
+
logger.info(`Updated to latest Redpanda RC version: ${LatestRedpandaVersion.latestRcRelease.version} with commit: ${LatestRedpandaVersion.latestRcRelease.commitHash}`)
|
|
46
|
+
}
|
|
42
47
|
})
|
|
43
48
|
|
|
44
49
|
if (!component.latest.asciidoc) {
|
|
45
50
|
component.latest.asciidoc = { attributes: {} }
|
|
46
51
|
}
|
|
47
52
|
|
|
48
|
-
if (LatestRedpandaVersion && semver.valid(LatestRedpandaVersion
|
|
53
|
+
if (LatestRedpandaVersion && semver.valid(LatestRedpandaVersion.latestRedpandaRelease.version)) {
|
|
49
54
|
let currentVersion = component.latest.asciidoc.attributes['full-version'] || '0.0.0'
|
|
50
|
-
if (semver.gt(LatestRedpandaVersion
|
|
51
|
-
component.latest.asciidoc.attributes['full-version'] = `${LatestRedpandaVersion
|
|
52
|
-
component.latest.asciidoc.attributes['latest-release-commit'] = `${LatestRedpandaVersion
|
|
53
|
-
logger.info(`Updated to latest Redpanda version: ${LatestRedpandaVersion
|
|
55
|
+
if (semver.gt(LatestRedpandaVersion.latestRedpandaRelease.version, currentVersion)) {
|
|
56
|
+
component.latest.asciidoc.attributes['full-version'] = `${LatestRedpandaVersion.latestRedpandaRelease.version}@`
|
|
57
|
+
component.latest.asciidoc.attributes['latest-release-commit'] = `${LatestRedpandaVersion.latestRedpandaRelease.commitHash}@`
|
|
58
|
+
logger.info(`Updated to latest Redpanda version: ${LatestRedpandaVersion.latestRedpandaRelease.version} with commit: ${LatestRedpandaVersion.latestRedpandaRelease.commitHash}`)
|
|
54
59
|
}
|
|
55
60
|
}
|
|
56
61
|
|