diff --git a/.github/workflows/publish-to-npm.yaml b/.github/workflows/publish-to-npm.yaml index db784da..f31ed25 100644 --- a/.github/workflows/publish-to-npm.yaml +++ b/.github/workflows/publish-to-npm.yaml @@ -37,7 +37,7 @@ jobs: needs: publish strategy: matrix: - repo: ['redpanda-data/docs', 'redpanda-data/cloud-docs', 'redpanda-data/rp-connect-docs'] + repo: ['redpanda-data/docs', 'redpanda-data/cloud-docs', 'redpanda-data/rp-connect-docs', 'redpanda-data/api-docs'] runs-on: ubuntu-latest steps: - uses: aws-actions/configure-aws-credentials@v4 diff --git a/bin/doc-tools.js b/bin/doc-tools.js index 9e4f201..9e9fed0 100755 --- a/bin/doc-tools.js +++ b/bin/doc-tools.js @@ -4,9 +4,7 @@ const { execSync, spawnSync } = require('child_process'); const os = require('os'); const { Command, Option } = require('commander'); const path = require('path'); -const yaml = require('yaml'); const fs = require('fs'); -const handlebars = require('handlebars'); const { determineDocsBranch } = require('../cli-utils/self-managed-docs-branch.js'); const fetchFromGithub = require('../tools/fetch-from-github.js'); const { urlToXref } = require('../cli-utils/convert-doc-links.js'); @@ -1031,12 +1029,12 @@ automation }); automation - .command('property-docs') + .command('property-docs') .description( - 'Generate JSON and consolidated AsciiDoc partials for Redpanda configuration properties. ' + - 'By default, only extracts properties to JSON. Use --generate-partials to create consolidated ' + - 'AsciiDoc partials (including deprecated properties).' - ) + 'Generate JSON and consolidated AsciiDoc partials for Redpanda configuration properties. ' + + 'By default, only extracts properties to JSON. Use --generate-partials to create consolidated ' + + 'AsciiDoc partials (including deprecated properties).' + ) .option('--tag ', 'Git tag or branch to extract from', 'dev') .option('--diff ', 'Also diff autogenerated properties from to ') .option('--overrides ', 'Optional JSON file with property description overrides') @@ -1049,14 +1047,12 @@ automation .option('--template-deprecated-property ', 'Custom Handlebars template for individual deprecated property sections') .option('--generate-partials', 'Generate consolidated property partials (cluster-properties.adoc, topic-properties.adoc, etc.) in the partials directory') .option('--partials-dir ', 'Directory for property partials (relative to output-dir)', 'partials') - .action((options) => { - verifyPropertyDependencies(); + .action((options) => { + verifyPropertyDependencies(); // Validate cloud support dependencies if requested if (options.cloudSupport) { console.log('🔍 Validating cloud support dependencies...'); - - // Check for GITHUB_TOKEN, GH_TOKEN, or REDPANDA_GITHUB_TOKEN const token = process.env.GITHUB_TOKEN || process.env.GH_TOKEN || process.env.REDPANDA_GITHUB_TOKEN; if (!token) { console.error('❌ Cloud support requires GITHUB_TOKEN, GH_TOKEN, or REDPANDA_GITHUB_TOKEN environment variable'); @@ -1068,7 +1064,6 @@ automation console.error(' Or: export REDPANDA_GITHUB_TOKEN=your_token_here'); process.exit(1); } - console.log('📦 Cloud support enabled - Python dependencies will be validated during execution'); if (process.env.VIRTUAL_ENV) { console.log(` Using virtual environment: ${process.env.VIRTUAL_ENV}`); @@ -1077,87 +1072,88 @@ automation console.log('✅ GitHub token validated'); } - const newTag = options.tag; - const oldTag = options.diff; - const overridesPath = options.overrides; - const outputDir = options.outputDir; - const cwd = path.resolve(__dirname, '../tools/property-extractor'); - - const make = (tag, overrides, templates = {}, outDir = 'modules/reference/', tempDir = null) => { - console.log(`⏳ Building property docs for ${tag}${tempDir ? ' (for diff)' : ''}…`); - const args = ['build', `TAG=${tag}`]; - - // Pass all paths as environment variables for consistency - const env = { ...process.env }; - - if (overrides) { - env.OVERRIDES = path.resolve(overrides); - } - if (options.cloudSupport) { - env.CLOUD_SUPPORT = '1'; - } - if (templates.property) { - env.TEMPLATE_PROPERTY = path.resolve(templates.property); - } - if (templates.topicProperty) { - env.TEMPLATE_TOPIC_PROPERTY = path.resolve(templates.topicProperty); - } - if (templates.topicPropertyMappings) { - env.TEMPLATE_TOPIC_PROPERTY_MAPPINGS = path.resolve(templates.topicPropertyMappings); - } - if (templates.deprecated) { - env.TEMPLATE_DEPRECATED = path.resolve(templates.deprecated); - } - if (templates.deprecatedProperty) { - env.TEMPLATE_DEPRECATED_PROPERTY = path.resolve(templates.deprecatedProperty); - } - - if (tempDir) { - env.OUTPUT_JSON_DIR = path.resolve(tempDir, 'examples'); - env.OUTPUT_AUTOGENERATED_DIR = path.resolve(tempDir); - } else { - env.OUTPUT_JSON_DIR = path.resolve(outDir, 'examples'); - env.OUTPUT_AUTOGENERATED_DIR = path.resolve(outDir); - } - - // Partials generation options - if (options.generatePartials) { - env.GENERATE_PARTIALS = '1'; - env.OUTPUT_PARTIALS_DIR = path.resolve(outDir, options.partialsDir || 'partials'); - } - - const r = spawnSync('make', args, { cwd, stdio: 'inherit', env }); - if (r.error) { - console.error(`❌ ${r.error.message}`); - process.exit(1); - } - if (r.status !== 0) process.exit(r.status); - }; - - // Collect template options - const templates = { - property: options.templateProperty, - topicProperty: options.templateTopicProperty, - topicPropertyMappings: options.templateTopicPropertyMappings, - deprecated: options.templateDeprecated, - deprecatedProperty: options.templateDeprecatedProperty, - }; - + const newTag = options.tag; + let oldTag = options.diff; + const overridesPath = options.overrides; + const outputDir = options.outputDir; + const cwd = path.resolve(__dirname, '../tools/property-extractor'); + + // If --diff is not provided, try to get the latest-redpanda-tag from Antora attributes + if (!oldTag) { + oldTag = getAntoraValue('asciidoc.attributes.latest-redpanda-tag'); if (oldTag) { - // Build old version first so its JSON exists for the diff step - make(oldTag, overridesPath, templates, outputDir, null); + console.log(`Using latest-redpanda-tag from Antora attributes for --diff: ${oldTag}`); + } else { + console.log('No --diff provided and no latest-redpanda-tag found in Antora attributes. Skipping diff.'); } + } - // Build new version - make(newTag, overridesPath, templates, outputDir, null); - - if (oldTag) { - // Generate property comparison report using the JSON now in outputDir/examples - generatePropertyComparisonReport(oldTag, newTag, outputDir); + const make = (tag, overrides, templates = {}, outDir = 'modules/reference/') => { + console.log(`⏳ Building property docs for ${tag}…`); + const args = ['build', `TAG=${tag}`]; + const env = { ...process.env }; + if (overrides) { + env.OVERRIDES = path.resolve(overrides); + } + if (options.cloudSupport) { + env.CLOUD_SUPPORT = '1'; + } + if (templates.property) { + env.TEMPLATE_PROPERTY = path.resolve(templates.property); + } + if (templates.topicProperty) { + env.TEMPLATE_TOPIC_PROPERTY = path.resolve(templates.topicProperty); + } + if (templates.topicPropertyMappings) { + env.TEMPLATE_TOPIC_PROPERTY_MAPPINGS = path.resolve(templates.topicPropertyMappings); + } + if (templates.deprecated) { + env.TEMPLATE_DEPRECATED = path.resolve(templates.deprecated); + } + if (templates.deprecatedProperty) { + env.TEMPLATE_DEPRECATED_PROPERTY = path.resolve(templates.deprecatedProperty); } + env.OUTPUT_JSON_DIR = path.resolve(outDir, 'examples'); + env.OUTPUT_AUTOGENERATED_DIR = path.resolve(outDir); + if (options.generatePartials) { + env.GENERATE_PARTIALS = '1'; + env.OUTPUT_PARTIALS_DIR = path.resolve(outDir, options.partialsDir || 'partials'); + } + const r = spawnSync('make', args, { cwd, stdio: 'inherit', env }); + if (r.error) { + console.error(`❌ ${r.error.message}`); + process.exit(1); + } + if (r.status !== 0) process.exit(r.status); + }; + + const templates = { + property: options.templateProperty, + topicProperty: options.templateTopicProperty, + topicPropertyMappings: options.templateTopicPropertyMappings, + deprecated: options.templateDeprecated, + deprecatedProperty: options.templateDeprecatedProperty, + }; + + const tagsAreSame = oldTag && newTag && oldTag === newTag; + if (oldTag && !tagsAreSame) { + make(oldTag, overridesPath, templates, outputDir); + } + make(newTag, overridesPath, templates, outputDir); + if (oldTag && !tagsAreSame) { + generatePropertyComparisonReport(oldTag, newTag, outputDir); + } else if (tagsAreSame) { + console.log('--diff and --tag are the same. Skipping diff and Antora config update.'); + } - process.exit(0); - }); + // If we used Antora's latest-redpanda-tag for diff, update it to the new tag + if (!options.diff && !tagsAreSame) { + setAntoraValue('asciidoc.attributes.latest-redpanda-tag', newTag); + console.log(`✅ Updated Antora latest-redpanda-tag to: ${newTag}`); + } + + process.exit(0); + }); automation .command('rpk-docs') diff --git a/cli-utils/antora-utils.js b/cli-utils/antora-utils.js index 4a92d3e..2ebbd1c 100644 --- a/cli-utils/antora-utils.js +++ b/cli-utils/antora-utils.js @@ -10,9 +10,17 @@ const yaml = require('js-yaml'); * @returns {Object|undefined} The parsed YAML as a JavaScript object, or undefined if not found or on error. */ function loadAntoraConfig() { - const antoraPath = path.join(process.cwd(), 'antora.yml'); - if (!fs.existsSync(antoraPath)) { - // No antora.yml in project root + // Support both antora.yml and antora.yaml + const cwd = process.cwd(); + const ymlPath = path.join(cwd, 'antora.yml'); + const yamlPath = path.join(cwd, 'antora.yaml'); + let antoraPath; + if (fs.existsSync(ymlPath)) { + antoraPath = ymlPath; + } else if (fs.existsSync(yamlPath)) { + antoraPath = yamlPath; + } else { + // No antora.yml or antora.yaml in project root return undefined; } @@ -20,12 +28,12 @@ function loadAntoraConfig() { const fileContents = fs.readFileSync(antoraPath, 'utf8'); const config = yaml.load(fileContents); if (typeof config !== 'object' || config === null) { - console.error('Warning: antora.yml parsed to a non‐object value.'); + console.error(`Warning: ${path.basename(antoraPath)} parsed to a non‐object value.`); return undefined; } return config; } catch (err) { - console.error(`Error reading/parsing antora.yml: ${err.message}`); + console.error(`Error reading/parsing ${path.basename(antoraPath)}: ${err.message}`); return undefined; } } @@ -74,9 +82,17 @@ function getAntoraValue(keyPath) { * True if it succeeded, false otherwise. */ function setAntoraValue(keyPath, newValue) { - const antoraPath = path.join(process.cwd(), 'antora.yml'); - if (!fs.existsSync(antoraPath)) { - console.error('Cannot update antora.yml: file not found in project root.'); + // Support both antora.yml and antora.yaml + const cwd = process.cwd(); + const ymlPath = path.join(cwd, 'antora.yml'); + const yamlPath = path.join(cwd, 'antora.yaml'); + let antoraPath; + if (fs.existsSync(ymlPath)) { + antoraPath = ymlPath; + } else if (fs.existsSync(yamlPath)) { + antoraPath = yamlPath; + } else { + console.error('Cannot update antora.yml or antora.yaml: file not found in project root.'); return false; } @@ -88,7 +104,7 @@ function setAntoraValue(keyPath, newValue) { config = {}; } } catch (err) { - console.error(`Error reading/parsing antora.yml: ${err.message}`); + console.error(`Error reading/parsing ${path.basename(antoraPath)}: ${err.message}`); return false; } @@ -115,7 +131,37 @@ function setAntoraValue(keyPath, newValue) { fs.writeFileSync(antoraPath, newYaml, 'utf8'); return true; } catch (err) { - console.error(`Error writing antora.yml: ${err.message}`); + console.error(`Error writing ${path.basename(antoraPath)}: ${err.message}`); + return false; + } +} + +/** + * Look for antora.yml in the current working directory + * (the project's root), load it if present, and return + * its `prerelease` value (boolean). If missing or on error, + * returns false. + */ +function getPrereleaseFromAntora() { + // Support both antora.yml and antora.yaml + const cwd = process.cwd(); + const ymlPath = path.join(cwd, 'antora.yml'); + const yamlPath = path.join(cwd, 'antora.yaml'); + let antoraPath; + if (fs.existsSync(ymlPath)) { + antoraPath = ymlPath; + } else if (fs.existsSync(yamlPath)) { + antoraPath = yamlPath; + } else { + return false; + } + + try { + const fileContents = fs.readFileSync(antoraPath, 'utf8'); + const antoraConfig = yaml.load(fileContents); + return antoraConfig.prerelease === true; + } catch (error) { + console.error(`Error reading ${path.basename(antoraPath)}:`, error.message); return false; } } @@ -124,4 +170,5 @@ module.exports = { loadAntoraConfig, getAntoraValue, setAntoraValue, + getPrereleaseFromAntora }; diff --git a/cli-utils/beta-from-antora.js b/cli-utils/beta-from-antora.js deleted file mode 100755 index 8e767e2..0000000 --- a/cli-utils/beta-from-antora.js +++ /dev/null @@ -1,27 +0,0 @@ -const fs = require('fs'); -const path = require('path'); -const yaml = require('js-yaml'); - -/** - * Look for antora.yml in the current working directory - * (the project's root), load it if present, and return - * its `prerelease` value (boolean). If missing or on error, - * returns false. - */ -function getPrereleaseFromAntora() { - const antoraPath = path.join(process.cwd(), 'antora.yml'); - if (!fs.existsSync(antoraPath)) { - return false; - } - - try { - const fileContents = fs.readFileSync(antoraPath, 'utf8'); - const antoraConfig = yaml.load(fileContents); - return antoraConfig.prerelease === true; - } catch (error) { - console.error('Error reading antora.yml:', error.message); - return false; - } -} - -module.exports = { getPrereleaseFromAntora }; diff --git a/package-lock.json b/package-lock.json index f62cef7..4634ef9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@redpanda-data/docs-extensions-and-macros", - "version": "4.10.6", + "version": "4.10.7", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@redpanda-data/docs-extensions-and-macros", - "version": "4.10.6", + "version": "4.10.7", "license": "ISC", "dependencies": { "@asciidoctor/tabs": "^1.0.0-beta.6", diff --git a/package.json b/package.json index 47121b5..1551204 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@redpanda-data/docs-extensions-and-macros", - "version": "4.10.6", + "version": "4.10.7", "description": "Antora extensions and macros developed for Redpanda documentation.", "keywords": [ "antora", diff --git a/tools/get-console-version.js b/tools/get-console-version.js index f4f0e65..31dccdf 100644 --- a/tools/get-console-version.js +++ b/tools/get-console-version.js @@ -4,7 +4,7 @@ const yaml = require('js-yaml'); const fs = require('fs'); const path = require('path'); const GetLatestConsoleVersion = require('../extensions/version-fetcher/get-latest-console-version.js'); -const { getPrereleaseFromAntora } = require('../cli-utils/beta-from-antora.js'); +const { getPrereleaseFromAntora } = require('../cli-utils/antora-utils.js'); /** * Fetches and prints the latest Console version and Docker repo. diff --git a/tools/get-redpanda-version.js b/tools/get-redpanda-version.js index 4a0a753..218e5b8 100644 --- a/tools/get-redpanda-version.js +++ b/tools/get-redpanda-version.js @@ -1,7 +1,7 @@ #!/usr/bin/env node const GetLatestRedpandaVersion = require('../extensions/version-fetcher/get-latest-redpanda-version.js'); -const { getPrereleaseFromAntora } = require('../cli-utils/beta-from-antora.js'); +const { getPrereleaseFromAntora } = require('../cli-utils/antora-utils.js'); /** * Fetches and prints the latest Redpanda version and Docker repository.