@redpanda-data/docs-extensions-and-macros 3.4.0 → 3.4.2

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.
@@ -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
- 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
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
- docs.forEach((docPage) => {
18
- const related = findRelated(docPage, sourceCategoryList, sourceDeploymentType, logger)
19
- related && relatedDocs.push(related)
20
- })
21
- labs.forEach((targetLabPage) => {
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.push(related);
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 (relatedDocs.length > 0) {
30
- labPage.asciidoc.attributes['page-related-docs'] = JSON.stringify(relatedDocs);
48
+ if (uniqueRelatedDocs.length > 0) {
49
+ labPage.asciidoc.attributes['page-related-docs'] = JSON.stringify(uniqueRelatedDocs);
31
50
  }
32
51
 
33
- if (relatedLabs.length > 0) {
34
- labPage.asciidoc.attributes['page-related-labs'] = JSON.stringify(relatedLabs);
52
+ if (uniqueRelatedLabs.length > 0) {
53
+ labPage.asciidoc.attributes['page-related-labs'] = JSON.stringify(uniqueRelatedLabs);
35
54
  }
36
55
 
37
- if (relatedDocs.length > 0 || relatedLabs.length > 0) {
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 (attributes) {
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 (sourcePageCategories, targetPageCategories) {
69
- return sourcePageCategories.every((category) => targetPageCategories.includes(category))
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
- this
5
- .on('navigationBuilt', ({ navigationCatalog, contentCatalog }) => {
6
- contentCatalog.getComponents().forEach(({ versions }) => {
7
- versions.forEach(({ name: component, version, navigation: nav, url: defaultUrl }) => {
8
- if (component === 'api') return;
9
- if (!nav) return
10
- const navEntriesByUrl = getNavEntriesByUrl(nav)
11
- const unlistedPages = contentCatalog
12
- .findBy({ component, version, family: 'page' })
13
- .filter((page) => page.out)
14
- .reduce((collector, page) => {
15
- if ((page.pub.url in navEntriesByUrl) || page.pub.url === defaultUrl) return collector
16
- logger.warn({ file: page.src, source: page.src.origin }, 'detected unlisted page')
17
- return collector.concat(page)
18
- }, [])
19
- if (unlistedPages.length && component === 'redpanda-connect') {
20
- // 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.
21
- addRedpandaConnectPagesToNav(nav[0].items, unlistedPages)
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
- function getNavEntriesByUrl (items = [], accum = {}) {
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
- contentCatalog.getPages((page) => page.out).forEach((page) => {
4
- if (page.asciidoc?.attributes['page-unpublish'] != null || page.asciidoc?.attributes['page-layout'] === 'api-partial') {
5
- delete page.out
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) && !tag.match(/rc\d+$/)) // Exclude 'rc-{number}' suffixes
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[0];
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
- return [latestRedpandaReleaseVersion, latestRedpandaReleaseCommitHash.substring(0, 7)];
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 [null, null];
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 [null, null];
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[0])) {
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[0], currentVersion)) {
51
- component.latest.asciidoc.attributes['full-version'] = `${LatestRedpandaVersion[0]}@`
52
- component.latest.asciidoc.attributes['latest-release-commit'] = `${LatestRedpandaVersion[1]}@`
53
- logger.info(`Updated to latest Redpanda version: ${LatestRedpandaVersion[0]} with commit: ${LatestRedpandaVersion[1]}`)
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
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@redpanda-data/docs-extensions-and-macros",
3
- "version": "3.4.0",
3
+ "version": "3.4.2",
4
4
  "description": "Antora extensions and macros developed for Redpanda documentation.",
5
5
  "keywords": [
6
6
  "antora",