@redpanda-data/docs-extensions-and-macros 4.10.5 → 4.10.7

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/bin/doc-tools.js CHANGED
@@ -4,9 +4,7 @@ const { execSync, spawnSync } = require('child_process');
4
4
  const os = require('os');
5
5
  const { Command, Option } = require('commander');
6
6
  const path = require('path');
7
- const yaml = require('yaml');
8
7
  const fs = require('fs');
9
- const handlebars = require('handlebars');
10
8
  const { determineDocsBranch } = require('../cli-utils/self-managed-docs-branch.js');
11
9
  const fetchFromGithub = require('../tools/fetch-from-github.js');
12
10
  const { urlToXref } = require('../cli-utils/convert-doc-links.js');
@@ -1031,12 +1029,12 @@ automation
1031
1029
  });
1032
1030
 
1033
1031
  automation
1034
- .command('property-docs')
1032
+ .command('property-docs')
1035
1033
  .description(
1036
- 'Generate JSON and consolidated AsciiDoc partials for Redpanda configuration properties. ' +
1037
- 'By default, only extracts properties to JSON. Use --generate-partials to create consolidated ' +
1038
- 'AsciiDoc partials (including deprecated properties).'
1039
- )
1034
+ 'Generate JSON and consolidated AsciiDoc partials for Redpanda configuration properties. ' +
1035
+ 'By default, only extracts properties to JSON. Use --generate-partials to create consolidated ' +
1036
+ 'AsciiDoc partials (including deprecated properties).'
1037
+ )
1040
1038
  .option('--tag <tag>', 'Git tag or branch to extract from', 'dev')
1041
1039
  .option('--diff <oldTag>', 'Also diff autogenerated properties from <oldTag> to <tag>')
1042
1040
  .option('--overrides <path>', 'Optional JSON file with property description overrides')
@@ -1049,14 +1047,12 @@ automation
1049
1047
  .option('--template-deprecated-property <path>', 'Custom Handlebars template for individual deprecated property sections')
1050
1048
  .option('--generate-partials', 'Generate consolidated property partials (cluster-properties.adoc, topic-properties.adoc, etc.) in the partials directory')
1051
1049
  .option('--partials-dir <path>', 'Directory for property partials (relative to output-dir)', 'partials')
1052
- .action((options) => {
1053
- verifyPropertyDependencies();
1050
+ .action((options) => {
1051
+ verifyPropertyDependencies();
1054
1052
 
1055
1053
  // Validate cloud support dependencies if requested
1056
1054
  if (options.cloudSupport) {
1057
1055
  console.log('🔍 Validating cloud support dependencies...');
1058
-
1059
- // Check for GITHUB_TOKEN, GH_TOKEN, or REDPANDA_GITHUB_TOKEN
1060
1056
  const token = process.env.GITHUB_TOKEN || process.env.GH_TOKEN || process.env.REDPANDA_GITHUB_TOKEN;
1061
1057
  if (!token) {
1062
1058
  console.error('❌ Cloud support requires GITHUB_TOKEN, GH_TOKEN, or REDPANDA_GITHUB_TOKEN environment variable');
@@ -1068,7 +1064,6 @@ automation
1068
1064
  console.error(' Or: export REDPANDA_GITHUB_TOKEN=your_token_here');
1069
1065
  process.exit(1);
1070
1066
  }
1071
-
1072
1067
  console.log('📦 Cloud support enabled - Python dependencies will be validated during execution');
1073
1068
  if (process.env.VIRTUAL_ENV) {
1074
1069
  console.log(` Using virtual environment: ${process.env.VIRTUAL_ENV}`);
@@ -1077,87 +1072,88 @@ automation
1077
1072
  console.log('✅ GitHub token validated');
1078
1073
  }
1079
1074
 
1080
- const newTag = options.tag;
1081
- const oldTag = options.diff;
1082
- const overridesPath = options.overrides;
1083
- const outputDir = options.outputDir;
1084
- const cwd = path.resolve(__dirname, '../tools/property-extractor');
1085
-
1086
- const make = (tag, overrides, templates = {}, outDir = 'modules/reference/', tempDir = null) => {
1087
- console.log(`⏳ Building property docs for ${tag}${tempDir ? ' (for diff)' : ''}…`);
1088
- const args = ['build', `TAG=${tag}`];
1089
-
1090
- // Pass all paths as environment variables for consistency
1091
- const env = { ...process.env };
1092
-
1093
- if (overrides) {
1094
- env.OVERRIDES = path.resolve(overrides);
1095
- }
1096
- if (options.cloudSupport) {
1097
- env.CLOUD_SUPPORT = '1';
1098
- }
1099
- if (templates.property) {
1100
- env.TEMPLATE_PROPERTY = path.resolve(templates.property);
1101
- }
1102
- if (templates.topicProperty) {
1103
- env.TEMPLATE_TOPIC_PROPERTY = path.resolve(templates.topicProperty);
1104
- }
1105
- if (templates.topicPropertyMappings) {
1106
- env.TEMPLATE_TOPIC_PROPERTY_MAPPINGS = path.resolve(templates.topicPropertyMappings);
1107
- }
1108
- if (templates.deprecated) {
1109
- env.TEMPLATE_DEPRECATED = path.resolve(templates.deprecated);
1110
- }
1111
- if (templates.deprecatedProperty) {
1112
- env.TEMPLATE_DEPRECATED_PROPERTY = path.resolve(templates.deprecatedProperty);
1113
- }
1114
-
1115
- if (tempDir) {
1116
- env.OUTPUT_JSON_DIR = path.resolve(tempDir, 'examples');
1117
- env.OUTPUT_AUTOGENERATED_DIR = path.resolve(tempDir);
1118
- } else {
1119
- env.OUTPUT_JSON_DIR = path.resolve(outDir, 'examples');
1120
- env.OUTPUT_AUTOGENERATED_DIR = path.resolve(outDir);
1121
- }
1122
-
1123
- // Partials generation options
1124
- if (options.generatePartials) {
1125
- env.GENERATE_PARTIALS = '1';
1126
- env.OUTPUT_PARTIALS_DIR = path.resolve(outDir, options.partialsDir || 'partials');
1127
- }
1128
-
1129
- const r = spawnSync('make', args, { cwd, stdio: 'inherit', env });
1130
- if (r.error) {
1131
- console.error(`❌ ${r.error.message}`);
1132
- process.exit(1);
1133
- }
1134
- if (r.status !== 0) process.exit(r.status);
1135
- };
1136
-
1137
- // Collect template options
1138
- const templates = {
1139
- property: options.templateProperty,
1140
- topicProperty: options.templateTopicProperty,
1141
- topicPropertyMappings: options.templateTopicPropertyMappings,
1142
- deprecated: options.templateDeprecated,
1143
- deprecatedProperty: options.templateDeprecatedProperty,
1144
- };
1145
-
1075
+ const newTag = options.tag;
1076
+ let oldTag = options.diff;
1077
+ const overridesPath = options.overrides;
1078
+ const outputDir = options.outputDir;
1079
+ const cwd = path.resolve(__dirname, '../tools/property-extractor');
1080
+
1081
+ // If --diff is not provided, try to get the latest-redpanda-tag from Antora attributes
1082
+ if (!oldTag) {
1083
+ oldTag = getAntoraValue('asciidoc.attributes.latest-redpanda-tag');
1146
1084
  if (oldTag) {
1147
- // Build old version first so its JSON exists for the diff step
1148
- make(oldTag, overridesPath, templates, outputDir, null);
1085
+ console.log(`Using latest-redpanda-tag from Antora attributes for --diff: ${oldTag}`);
1086
+ } else {
1087
+ console.log('No --diff provided and no latest-redpanda-tag found in Antora attributes. Skipping diff.');
1149
1088
  }
1089
+ }
1150
1090
 
1151
- // Build new version
1152
- make(newTag, overridesPath, templates, outputDir, null);
1153
-
1154
- if (oldTag) {
1155
- // Generate property comparison report using the JSON now in outputDir/examples
1156
- generatePropertyComparisonReport(oldTag, newTag, outputDir);
1091
+ const make = (tag, overrides, templates = {}, outDir = 'modules/reference/') => {
1092
+ console.log(`⏳ Building property docs for ${tag}…`);
1093
+ const args = ['build', `TAG=${tag}`];
1094
+ const env = { ...process.env };
1095
+ if (overrides) {
1096
+ env.OVERRIDES = path.resolve(overrides);
1097
+ }
1098
+ if (options.cloudSupport) {
1099
+ env.CLOUD_SUPPORT = '1';
1100
+ }
1101
+ if (templates.property) {
1102
+ env.TEMPLATE_PROPERTY = path.resolve(templates.property);
1103
+ }
1104
+ if (templates.topicProperty) {
1105
+ env.TEMPLATE_TOPIC_PROPERTY = path.resolve(templates.topicProperty);
1106
+ }
1107
+ if (templates.topicPropertyMappings) {
1108
+ env.TEMPLATE_TOPIC_PROPERTY_MAPPINGS = path.resolve(templates.topicPropertyMappings);
1109
+ }
1110
+ if (templates.deprecated) {
1111
+ env.TEMPLATE_DEPRECATED = path.resolve(templates.deprecated);
1112
+ }
1113
+ if (templates.deprecatedProperty) {
1114
+ env.TEMPLATE_DEPRECATED_PROPERTY = path.resolve(templates.deprecatedProperty);
1157
1115
  }
1116
+ env.OUTPUT_JSON_DIR = path.resolve(outDir, 'examples');
1117
+ env.OUTPUT_AUTOGENERATED_DIR = path.resolve(outDir);
1118
+ if (options.generatePartials) {
1119
+ env.GENERATE_PARTIALS = '1';
1120
+ env.OUTPUT_PARTIALS_DIR = path.resolve(outDir, options.partialsDir || 'partials');
1121
+ }
1122
+ const r = spawnSync('make', args, { cwd, stdio: 'inherit', env });
1123
+ if (r.error) {
1124
+ console.error(`❌ ${r.error.message}`);
1125
+ process.exit(1);
1126
+ }
1127
+ if (r.status !== 0) process.exit(r.status);
1128
+ };
1129
+
1130
+ const templates = {
1131
+ property: options.templateProperty,
1132
+ topicProperty: options.templateTopicProperty,
1133
+ topicPropertyMappings: options.templateTopicPropertyMappings,
1134
+ deprecated: options.templateDeprecated,
1135
+ deprecatedProperty: options.templateDeprecatedProperty,
1136
+ };
1137
+
1138
+ const tagsAreSame = oldTag && newTag && oldTag === newTag;
1139
+ if (oldTag && !tagsAreSame) {
1140
+ make(oldTag, overridesPath, templates, outputDir);
1141
+ }
1142
+ make(newTag, overridesPath, templates, outputDir);
1143
+ if (oldTag && !tagsAreSame) {
1144
+ generatePropertyComparisonReport(oldTag, newTag, outputDir);
1145
+ } else if (tagsAreSame) {
1146
+ console.log('--diff and --tag are the same. Skipping diff and Antora config update.');
1147
+ }
1158
1148
 
1159
- process.exit(0);
1160
- });
1149
+ // If we used Antora's latest-redpanda-tag for diff, update it to the new tag
1150
+ if (!options.diff && !tagsAreSame) {
1151
+ setAntoraValue('asciidoc.attributes.latest-redpanda-tag', newTag);
1152
+ console.log(`✅ Updated Antora latest-redpanda-tag to: ${newTag}`);
1153
+ }
1154
+
1155
+ process.exit(0);
1156
+ });
1161
1157
 
1162
1158
  automation
1163
1159
  .command('rpk-docs')
@@ -10,9 +10,17 @@ const yaml = require('js-yaml');
10
10
  * @returns {Object|undefined} The parsed YAML as a JavaScript object, or undefined if not found or on error.
11
11
  */
12
12
  function loadAntoraConfig() {
13
- const antoraPath = path.join(process.cwd(), 'antora.yml');
14
- if (!fs.existsSync(antoraPath)) {
15
- // No antora.yml in project root
13
+ // Support both antora.yml and antora.yaml
14
+ const cwd = process.cwd();
15
+ const ymlPath = path.join(cwd, 'antora.yml');
16
+ const yamlPath = path.join(cwd, 'antora.yaml');
17
+ let antoraPath;
18
+ if (fs.existsSync(ymlPath)) {
19
+ antoraPath = ymlPath;
20
+ } else if (fs.existsSync(yamlPath)) {
21
+ antoraPath = yamlPath;
22
+ } else {
23
+ // No antora.yml or antora.yaml in project root
16
24
  return undefined;
17
25
  }
18
26
 
@@ -20,12 +28,12 @@ function loadAntoraConfig() {
20
28
  const fileContents = fs.readFileSync(antoraPath, 'utf8');
21
29
  const config = yaml.load(fileContents);
22
30
  if (typeof config !== 'object' || config === null) {
23
- console.error('Warning: antora.yml parsed to a non‐object value.');
31
+ console.error(`Warning: ${path.basename(antoraPath)} parsed to a non‐object value.`);
24
32
  return undefined;
25
33
  }
26
34
  return config;
27
35
  } catch (err) {
28
- console.error(`Error reading/parsing antora.yml: ${err.message}`);
36
+ console.error(`Error reading/parsing ${path.basename(antoraPath)}: ${err.message}`);
29
37
  return undefined;
30
38
  }
31
39
  }
@@ -74,9 +82,17 @@ function getAntoraValue(keyPath) {
74
82
  * True if it succeeded, false otherwise.
75
83
  */
76
84
  function setAntoraValue(keyPath, newValue) {
77
- const antoraPath = path.join(process.cwd(), 'antora.yml');
78
- if (!fs.existsSync(antoraPath)) {
79
- console.error('Cannot update antora.yml: file not found in project root.');
85
+ // Support both antora.yml and antora.yaml
86
+ const cwd = process.cwd();
87
+ const ymlPath = path.join(cwd, 'antora.yml');
88
+ const yamlPath = path.join(cwd, 'antora.yaml');
89
+ let antoraPath;
90
+ if (fs.existsSync(ymlPath)) {
91
+ antoraPath = ymlPath;
92
+ } else if (fs.existsSync(yamlPath)) {
93
+ antoraPath = yamlPath;
94
+ } else {
95
+ console.error('Cannot update antora.yml or antora.yaml: file not found in project root.');
80
96
  return false;
81
97
  }
82
98
 
@@ -88,7 +104,7 @@ function setAntoraValue(keyPath, newValue) {
88
104
  config = {};
89
105
  }
90
106
  } catch (err) {
91
- console.error(`Error reading/parsing antora.yml: ${err.message}`);
107
+ console.error(`Error reading/parsing ${path.basename(antoraPath)}: ${err.message}`);
92
108
  return false;
93
109
  }
94
110
 
@@ -115,7 +131,37 @@ function setAntoraValue(keyPath, newValue) {
115
131
  fs.writeFileSync(antoraPath, newYaml, 'utf8');
116
132
  return true;
117
133
  } catch (err) {
118
- console.error(`Error writing antora.yml: ${err.message}`);
134
+ console.error(`Error writing ${path.basename(antoraPath)}: ${err.message}`);
135
+ return false;
136
+ }
137
+ }
138
+
139
+ /**
140
+ * Look for antora.yml in the current working directory
141
+ * (the project's root), load it if present, and return
142
+ * its `prerelease` value (boolean). If missing or on error,
143
+ * returns false.
144
+ */
145
+ function getPrereleaseFromAntora() {
146
+ // Support both antora.yml and antora.yaml
147
+ const cwd = process.cwd();
148
+ const ymlPath = path.join(cwd, 'antora.yml');
149
+ const yamlPath = path.join(cwd, 'antora.yaml');
150
+ let antoraPath;
151
+ if (fs.existsSync(ymlPath)) {
152
+ antoraPath = ymlPath;
153
+ } else if (fs.existsSync(yamlPath)) {
154
+ antoraPath = yamlPath;
155
+ } else {
156
+ return false;
157
+ }
158
+
159
+ try {
160
+ const fileContents = fs.readFileSync(antoraPath, 'utf8');
161
+ const antoraConfig = yaml.load(fileContents);
162
+ return antoraConfig.prerelease === true;
163
+ } catch (error) {
164
+ console.error(`Error reading ${path.basename(antoraPath)}:`, error.message);
119
165
  return false;
120
166
  }
121
167
  }
@@ -124,4 +170,5 @@ module.exports = {
124
170
  loadAntoraConfig,
125
171
  getAntoraValue,
126
172
  setAntoraValue,
173
+ getPrereleaseFromAntora
127
174
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@redpanda-data/docs-extensions-and-macros",
3
- "version": "4.10.5",
3
+ "version": "4.10.7",
4
4
  "description": "Antora extensions and macros developed for Redpanda documentation.",
5
5
  "keywords": [
6
6
  "antora",
@@ -28,21 +28,15 @@ function normalizeTag(tag) {
28
28
  throw new Error('Invalid version format: tag cannot be empty');
29
29
  }
30
30
 
31
- // Handle dev branch
32
- if (tag === 'dev') {
33
- return 'dev';
34
- }
35
-
36
31
  // Remove 'v' prefix if present
37
32
  const normalized = tag.startsWith('v') ? tag.slice(1) : tag;
38
-
39
33
  // Validate semantic version format
40
34
  const semverPattern = /^\d+\.\d+\.\d+(-[\w\.-]+)?(\+[\w\.-]+)?$/;
41
- if (!semverPattern.test(normalized) && normalized !== 'dev') {
42
- throw new Error(`Invalid version format: ${tag}. Expected format like v25.1.1 or 25.1.1`);
35
+ if (semverPattern.test(normalized)) {
36
+ return normalized;
43
37
  }
44
-
45
- return normalized;
38
+ // If not a valid semver, treat as branch name and return as-is
39
+ return tag;
46
40
  }
47
41
 
48
42
  /**
@@ -59,22 +53,20 @@ function getMajorMinor(version) {
59
53
  throw new Error('Version must be a non-empty string');
60
54
  }
61
55
 
62
- if (version === 'dev') {
63
- return 'dev';
56
+ // Only process if valid semver, else return as-is (branch name)
57
+ const semverPattern = /^\d+\.\d+\.\d+(-[\w\.-]+)?(\+[\w\.-]+)?$/;
58
+ if (!semverPattern.test(version)) {
59
+ return version;
64
60
  }
65
-
66
61
  const parts = version.split('.');
67
62
  if (parts.length < 2) {
68
63
  throw new Error(`Invalid version format: ${version}. Expected X.Y.Z format`);
69
64
  }
70
-
71
65
  const major = parseInt(parts[0], 10);
72
66
  const minor = parseInt(parts[1], 10);
73
-
74
67
  if (isNaN(major) || isNaN(minor)) {
75
68
  throw new Error(`Major and minor versions must be numbers: ${version}`);
76
69
  }
77
-
78
70
  return `${major}.${minor}`;
79
71
  }
80
72
 
@@ -4,7 +4,7 @@ const yaml = require('js-yaml');
4
4
  const fs = require('fs');
5
5
  const path = require('path');
6
6
  const GetLatestConsoleVersion = require('../extensions/version-fetcher/get-latest-console-version.js');
7
- const { getPrereleaseFromAntora } = require('../cli-utils/beta-from-antora.js');
7
+ const { getPrereleaseFromAntora } = require('../cli-utils/antora-utils.js');
8
8
 
9
9
  /**
10
10
  * Fetches and prints the latest Console version and Docker repo.
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  const GetLatestRedpandaVersion = require('../extensions/version-fetcher/get-latest-redpanda-version.js');
4
- const { getPrereleaseFromAntora } = require('../cli-utils/beta-from-antora.js');
4
+ const { getPrereleaseFromAntora } = require('../cli-utils/antora-utils.js');
5
5
 
6
6
  /**
7
7
  * Fetches and prints the latest Redpanda version and Docker repository.
@@ -1,27 +0,0 @@
1
- const fs = require('fs');
2
- const path = require('path');
3
- const yaml = require('js-yaml');
4
-
5
- /**
6
- * Look for antora.yml in the current working directory
7
- * (the project's root), load it if present, and return
8
- * its `prerelease` value (boolean). If missing or on error,
9
- * returns false.
10
- */
11
- function getPrereleaseFromAntora() {
12
- const antoraPath = path.join(process.cwd(), 'antora.yml');
13
- if (!fs.existsSync(antoraPath)) {
14
- return false;
15
- }
16
-
17
- try {
18
- const fileContents = fs.readFileSync(antoraPath, 'utf8');
19
- const antoraConfig = yaml.load(fileContents);
20
- return antoraConfig.prerelease === true;
21
- } catch (error) {
22
- console.error('Error reading antora.yml:', error.message);
23
- return false;
24
- }
25
- }
26
-
27
- module.exports = { getPrereleaseFromAntora };