Refactored the release notes generator

script with the following improvements:

  1. Renamed output formats:
    - standard → integrated: All repositories' changes are
  integrated together
    - core-enterprise → separated: Primary repository
  first, then secondary repositories
  2. Generalized the separated format:
    - Configurable primary repository (by name or index)
    - Customizable section labels and headers
    - Flexible template system for different products
  3. Enhanced configuration system:
    - Per-repository PR link settings
    - Full template customization for separated format
    - Support for configuration files
  4. Created example configurations:
    - config/influxdb3-core-enterprise.json: For InfluxDB 3
   Core/Enterprise releases
    - config/influxdb-v2.json: For InfluxDB v2 releases
    - config/influxdb3-clustered.json: For Clustered
  (Kubernetes operator) releases
  5. Updated documentation:
    - Explained both output formats clearly
    - Added configuration options documentation
    - Included example configurations usage

  The script now provides a flexible system that can handle
   various release note formats for different InfluxData
  products while maintaining the specific requirements like
   excluding PR links for influxdb_pro but including them
  for influxdb.
pull/6190/head
Jason Stirnaman 2025-07-07 10:11:00 -05:00
parent 85d8b29772
commit 33a2d5ce63
5 changed files with 366 additions and 160 deletions

View File

@ -0,0 +1,17 @@
{
"outputFormat": "integrated",
"repositories": [
{
"name": "influxdb",
"path": "../influxdb",
"label": "influxdb",
"includePrLinks": true
},
{
"name": "plutonium",
"path": "https://github.com/influxdata/plutonium",
"label": "enterprise_v1",
"includePrLinks": false
}
]
}

View File

@ -0,0 +1,11 @@
{
"outputFormat": "integrated",
"repositories": [
{
"name": "influxdb",
"path": "../influxdb",
"label": "influxdb",
"includePrLinks": true
}
]
}

View File

@ -0,0 +1,25 @@
{
"outputFormat": "separated",
"primaryRepo": "influxdb",
"repositories": [
{
"name": "influxdb",
"path": "../influxdb",
"label": "influxdb",
"includePrLinks": true
},
{
"name": "influxdb_pro",
"path": "../influxdb_pro",
"label": "influxdb_pro",
"includePrLinks": true
}
],
"separatedTemplate": {
"header": "> [!Note]\n> #### InfluxDB 3 Core and Enterprise relationship\n>\n> InfluxDB 3 Enterprise is a superset of InfluxDB 3 Core.\n> All updates to Core are automatically included in Enterprise.\n> The Enterprise sections below only list updates exclusive to Enterprise.",
"primaryLabel": "Core",
"secondaryLabel": "Enterprise",
"secondaryIntro": "All Core updates are included in Enterprise. Additional Enterprise-specific features and fixes:",
"comment": "If you plan to run .claude enhance-release-notes after this, you need to include PR links. For Enterprise, remove the links after running .claude enhance-release-notes."
}
}

View File

@ -19,14 +19,24 @@ const colors = {
// Default configuration // Default configuration
const DEFAULT_CONFIG = { const DEFAULT_CONFIG = {
outputFormat: 'standard', // 'standard' or 'core-enterprise' outputFormat: 'integrated', // 'integrated' or 'separated'
primaryRepo: null, // Index or name of primary repository (for separated format)
repositories: [ repositories: [
{ {
name: 'primary', name: 'primary',
path: null, // Will be set from command line path: null, // Will be set from command line
label: 'primary', label: 'primary',
includePrLinks: true, // Default to include PR links
}, },
], ],
// Template for separated format
separatedTemplate: {
header: null, // Optional header text/markdown
primaryLabel: 'Primary', // Label for primary section
secondaryLabel: 'Additional Changes', // Label for secondary section
secondaryIntro:
'All primary updates are included. Additional repository-specific features and fixes:', // Intro text for secondary
},
}; };
class ReleaseNotesGenerator { class ReleaseNotesGenerator {
@ -35,6 +45,7 @@ class ReleaseNotesGenerator {
this.toVersion = options.toVersion || 'v3.2.0'; this.toVersion = options.toVersion || 'v3.2.0';
this.fetchCommits = options.fetchCommits !== false; this.fetchCommits = options.fetchCommits !== false;
this.pullCommits = options.pullCommits || false; this.pullCommits = options.pullCommits || false;
this.includePrLinks = options.includePrLinks !== false; // Default to true
this.config = options.config || DEFAULT_CONFIG; this.config = options.config || DEFAULT_CONFIG;
this.outputDir = this.outputDir =
options.outputDir || join(__dirname, '..', 'output', 'release-notes'); options.outputDir || join(__dirname, '..', 'output', 'release-notes');
@ -356,7 +367,12 @@ class ReleaseNotesGenerator {
} }
// Enhance commit message with analysis of changes // Enhance commit message with analysis of changes
enhanceCommitMessage(repoPath, commitMessage, prNumber) { enhanceCommitMessage(
repoPath,
commitMessage,
prNumber,
includePrLinks = null
) {
// Extract the basic semantic prefix // Extract the basic semantic prefix
const semanticMatch = commitMessage.match( const semanticMatch = commitMessage.match(
/^(feat|fix|perf|refactor|style|test|docs|chore):\s*(.+)/ /^(feat|fix|perf|refactor|style|test|docs|chore):\s*(.+)/
@ -365,6 +381,9 @@ class ReleaseNotesGenerator {
const [, type, description] = semanticMatch; const [, type, description] = semanticMatch;
// Remove PR number from description if it's already there to avoid duplication
const cleanDescription = description.replace(/\s*\(#\d+\)$/g, '').trim();
// Get commit hash if available // Get commit hash if available
const hashMatch = commitMessage.match(/^([a-f0-9]+)\s+/); const hashMatch = commitMessage.match(/^([a-f0-9]+)\s+/);
const commitHash = hashMatch ? hashMatch[1] : null; const commitHash = hashMatch ? hashMatch[1] : null;
@ -372,13 +391,17 @@ class ReleaseNotesGenerator {
// Try to enhance based on the type and description // Try to enhance based on the type and description
const enhanced = this.generateEnhancedDescription( const enhanced = this.generateEnhancedDescription(
type, type,
description, cleanDescription,
repoPath, repoPath,
commitHash commitHash
); );
// If we have a PR number, include it // Use repository-specific setting if provided, otherwise use global setting
if (prNumber) { const shouldIncludePrLinks =
includePrLinks !== null ? includePrLinks : this.includePrLinks;
// If we have a PR number and should include PR links, include it
if (prNumber && shouldIncludePrLinks) {
return `${enhanced} ([#${prNumber}](https://github.com/influxdata/influxdb/pull/${prNumber}))`; return `${enhanced} ([#${prNumber}](https://github.com/influxdata/influxdb/pull/${prNumber}))`;
} }
@ -500,7 +523,8 @@ class ReleaseNotesGenerator {
const enhanced = this.enhanceCommitMessage( const enhanced = this.enhanceCommitMessage(
repo.path, repo.path,
line.replace(/^[a-f0-9]* /, ''), line.replace(/^[a-f0-9]* /, ''),
prNumber prNumber,
repo.includePrLinks
); );
return `- [${repoLabel}] ${enhanced}`; return `- [${repoLabel}] ${enhanced}`;
}); });
@ -508,7 +532,12 @@ class ReleaseNotesGenerator {
const featuresBody = this.getCommitsWithBody(repo.path, 'feat:').map( const featuresBody = this.getCommitsWithBody(repo.path, 'feat:').map(
(line) => { (line) => {
const prNumber = this.extractPrNumber(line); const prNumber = this.extractPrNumber(line);
const enhanced = this.enhanceCommitMessage(repo.path, line, prNumber); const enhanced = this.enhanceCommitMessage(
repo.path,
line,
prNumber,
repo.includePrLinks
);
return `- [${repoLabel}] ${enhanced}`; return `- [${repoLabel}] ${enhanced}`;
} }
); );
@ -524,7 +553,8 @@ class ReleaseNotesGenerator {
const enhanced = this.enhanceCommitMessage( const enhanced = this.enhanceCommitMessage(
repo.path, repo.path,
line.replace(/^[a-f0-9]* /, ''), line.replace(/^[a-f0-9]* /, ''),
prNumber prNumber,
repo.includePrLinks
); );
return `- [${repoLabel}] ${enhanced}`; return `- [${repoLabel}] ${enhanced}`;
}); });
@ -532,7 +562,12 @@ class ReleaseNotesGenerator {
const fixesBody = this.getCommitsWithBody(repo.path, 'fix:').map( const fixesBody = this.getCommitsWithBody(repo.path, 'fix:').map(
(line) => { (line) => {
const prNumber = this.extractPrNumber(line); const prNumber = this.extractPrNumber(line);
const enhanced = this.enhanceCommitMessage(repo.path, line, prNumber); const enhanced = this.enhanceCommitMessage(
repo.path,
line,
prNumber,
repo.includePrLinks
);
return `- [${repoLabel}] ${enhanced}`; return `- [${repoLabel}] ${enhanced}`;
} }
); );
@ -548,7 +583,8 @@ class ReleaseNotesGenerator {
const enhanced = this.enhanceCommitMessage( const enhanced = this.enhanceCommitMessage(
repo.path, repo.path,
line.replace(/^[a-f0-9]* /, ''), line.replace(/^[a-f0-9]* /, ''),
prNumber prNumber,
repo.includePrLinks
); );
return `- [${repoLabel}] ${enhanced}`; return `- [${repoLabel}] ${enhanced}`;
}); });
@ -556,7 +592,12 @@ class ReleaseNotesGenerator {
const perfBody = this.getCommitsWithBody(repo.path, 'perf:').map( const perfBody = this.getCommitsWithBody(repo.path, 'perf:').map(
(line) => { (line) => {
const prNumber = this.extractPrNumber(line); const prNumber = this.extractPrNumber(line);
const enhanced = this.enhanceCommitMessage(repo.path, line, prNumber); const enhanced = this.enhanceCommitMessage(
repo.path,
line,
prNumber,
repo.includePrLinks
);
return `- [${repoLabel}] ${enhanced}`; return `- [${repoLabel}] ${enhanced}`;
} }
); );
@ -583,8 +624,8 @@ class ReleaseNotesGenerator {
return results; return results;
} }
// Generate standard format release notes // Generate integrated format release notes
generateStandardFormat(commits, releaseDate) { generateIntegratedFormat(commits, releaseDate) {
const lines = []; const lines = [];
lines.push(`## ${this.toVersion} {date="${releaseDate}"}`); lines.push(`## ${this.toVersion} {date="${releaseDate}"}`);
@ -622,7 +663,7 @@ class ReleaseNotesGenerator {
commits.breaking.forEach((change) => { commits.breaking.forEach((change) => {
const pr = this.extractPrNumber(change); const pr = this.extractPrNumber(change);
const cleanLine = change.replace(/ \\(#\\d+\\)$/, ''); const cleanLine = change.replace(/ \\(#\\d+\\)$/, '');
if (pr) { if (pr && this.includePrLinks) {
lines.push( lines.push(
`${cleanLine} ([#${pr}](https://github.com/influxdata/influxdb/pull/${pr}))` `${cleanLine} ([#${pr}](https://github.com/influxdata/influxdb/pull/${pr}))`
); );
@ -651,7 +692,7 @@ class ReleaseNotesGenerator {
commits.api.forEach((api) => { commits.api.forEach((api) => {
const pr = this.extractPrNumber(api); const pr = this.extractPrNumber(api);
const cleanLine = api.replace(/ \\(#\\d+\\)$/, ''); const cleanLine = api.replace(/ \\(#\\d+\\)$/, '');
if (pr) { if (pr && this.includePrLinks) {
lines.push( lines.push(
`${cleanLine} ([#${pr}](https://github.com/influxdata/influxdb/pull/${pr}))` `${cleanLine} ([#${pr}](https://github.com/influxdata/influxdb/pull/${pr}))`
); );
@ -681,59 +722,81 @@ class ReleaseNotesGenerator {
return lines.join('\n'); return lines.join('\n');
} }
// Generate Core/Enterprise format release notes // Generate separated format release notes
generateCoreEnterpriseFormat(commits, releaseDate) { generateSeparatedFormat(commits, releaseDate) {
const lines = []; const lines = [];
// Add template note // Add custom header if provided
lines.push('> [!Note]'); if (this.config.separatedTemplate && this.config.separatedTemplate.header) {
lines.push('> #### InfluxDB 3 Core and Enterprise relationship'); lines.push(this.config.separatedTemplate.header);
lines.push('>'); lines.push('');
lines.push('> InfluxDB 3 Enterprise is a superset of InfluxDB 3 Core.'); }
lines.push(
'> All updates to Core are automatically included in Enterprise.'
);
lines.push(
'> The Enterprise sections below only list updates exclusive to Enterprise.'
);
lines.push('');
lines.push(`## ${this.toVersion} {date="${releaseDate}"}`); lines.push(`## ${this.toVersion} {date="${releaseDate}"}`);
lines.push(''); lines.push('');
// Separate commits by repository // Determine primary repository
const coreCommits = { let primaryRepoLabel = null;
features: commits.features if (this.config.primaryRepo !== null) {
.filter((f) => f.includes('[influxdb]')) // Find primary repo by index or name
.map((f) => f.replace('- [influxdb] ', '- ')), if (typeof this.config.primaryRepo === 'number') {
fixes: commits.fixes const primaryRepo = this.config.repositories[this.config.primaryRepo];
.filter((f) => f.includes('[influxdb]')) primaryRepoLabel = primaryRepo ? primaryRepo.label : null;
.map((f) => f.replace('- [influxdb] ', '- ')), } else {
perf: commits.perf const primaryRepo = this.config.repositories.find(
.filter((f) => f.includes('[influxdb]')) (r) => r.name === this.config.primaryRepo
.map((f) => f.replace('- [influxdb] ', '- ')), );
primaryRepoLabel = primaryRepo ? primaryRepo.label : null;
}
}
// If no primary specified, use the first repository
if (!primaryRepoLabel && this.config.repositories.length > 0) {
primaryRepoLabel = this.config.repositories[0].label;
}
// Separate commits by primary and secondary repositories
const primaryCommits = {
features: [],
fixes: [],
perf: [],
}; };
const enterpriseCommits = { const secondaryCommits = {
features: commits.features features: [],
.filter((f) => f.includes('[influxdb_pro]')) fixes: [],
.map((f) => f.replace('- [influxdb_pro] ', '- ')), perf: [],
fixes: commits.fixes
.filter((f) => f.includes('[influxdb_pro]'))
.map((f) => f.replace('- [influxdb_pro] ', '- ')),
perf: commits.perf
.filter((f) => f.includes('[influxdb_pro]'))
.map((f) => f.replace('- [influxdb_pro] ', '- ')),
}; };
// Core section // Sort commits into primary and secondary
lines.push('### Core'); for (const type of ['features', 'fixes', 'perf']) {
commits[type].forEach((commit) => {
// Extract repository label from commit
const labelMatch = commit.match(/^- \[([^\]]+)\]/);
if (labelMatch) {
const repoLabel = labelMatch[1];
const cleanCommit = commit.replace(/^- \[[^\]]+\] /, '- ');
if (repoLabel === primaryRepoLabel) {
primaryCommits[type].push(cleanCommit);
} else {
// Keep the label for secondary commits
secondaryCommits[type].push(commit);
}
}
});
}
// Primary section
const primaryLabel =
this.config.separatedTemplate?.primaryLabel || 'Primary';
lines.push(`### ${primaryLabel}`);
lines.push(''); lines.push('');
lines.push('#### Features'); lines.push('#### Features');
lines.push(''); lines.push('');
if (coreCommits.features.length > 0) { if (primaryCommits.features.length > 0) {
coreCommits.features.forEach((feature) => { primaryCommits.features.forEach((feature) => {
// Enhanced messages already include PR links
lines.push(feature); lines.push(feature);
}); });
} else { } else {
@ -744,81 +807,82 @@ class ReleaseNotesGenerator {
lines.push('#### Bug Fixes'); lines.push('#### Bug Fixes');
lines.push(''); lines.push('');
if (coreCommits.fixes.length > 0) { if (primaryCommits.fixes.length > 0) {
coreCommits.fixes.forEach((fix) => { primaryCommits.fixes.forEach((fix) => {
// Enhanced messages already include PR links
lines.push(fix); lines.push(fix);
}); });
} else { } else {
lines.push('- No bug fixes in this release'); lines.push('- No bug fixes in this release');
} }
// Core performance improvements if any // Primary performance improvements if any
if (coreCommits.perf.length > 0) { if (primaryCommits.perf.length > 0) {
lines.push(''); lines.push('');
lines.push('#### Performance Improvements'); lines.push('#### Performance Improvements');
lines.push(''); lines.push('');
coreCommits.perf.forEach((perf) => { primaryCommits.perf.forEach((perf) => {
// Enhanced messages already include PR links
lines.push(perf); lines.push(perf);
}); });
} }
// Enterprise section // Secondary section (only if there are secondary repositories)
lines.push(''); const hasSecondaryChanges =
lines.push('### Enterprise'); secondaryCommits.features.length > 0 ||
lines.push(''); secondaryCommits.fixes.length > 0 ||
lines.push( secondaryCommits.perf.length > 0;
'All Core updates are included in Enterprise. Additional Enterprise-specific features and fixes:'
);
lines.push('');
let hasEnterpriseChanges = false; if (this.config.repositories.length > 1) {
lines.push('');
const secondaryLabel =
this.config.separatedTemplate?.secondaryLabel || 'Additional Changes';
lines.push(`### ${secondaryLabel}`);
lines.push('');
// Enterprise features const secondaryIntro =
if (enterpriseCommits.features.length > 0) { this.config.separatedTemplate?.secondaryIntro ||
hasEnterpriseChanges = true; 'All primary updates are included. Additional repository-specific features and fixes:';
lines.push('#### Features'); lines.push(secondaryIntro);
lines.push(''); lines.push('');
enterpriseCommits.features.forEach((feature) => {
// Enhanced messages already include PR links
lines.push(feature);
});
lines.push('');
}
// Enterprise fixes // Secondary features
if (enterpriseCommits.fixes.length > 0) { if (secondaryCommits.features.length > 0) {
hasEnterpriseChanges = true; lines.push('#### Features');
lines.push('#### Bug Fixes'); lines.push('');
lines.push(''); secondaryCommits.features.forEach((feature) => {
enterpriseCommits.fixes.forEach((fix) => { lines.push(feature);
// Enhanced messages already include PR links });
lines.push(fix); lines.push('');
}); }
lines.push('');
}
// Enterprise performance improvements // Secondary fixes
if (enterpriseCommits.perf.length > 0) { if (secondaryCommits.fixes.length > 0) {
hasEnterpriseChanges = true; lines.push('#### Bug Fixes');
lines.push('#### Performance Improvements'); lines.push('');
lines.push(''); secondaryCommits.fixes.forEach((fix) => {
enterpriseCommits.perf.forEach((perf) => { lines.push(fix);
// Enhanced messages already include PR links });
lines.push(perf); lines.push('');
}); }
lines.push('');
}
// No Enterprise-specific changes message // Secondary performance improvements
if (!hasEnterpriseChanges) { if (secondaryCommits.perf.length > 0) {
lines.push('#### No Enterprise-specific changes'); lines.push('#### Performance Improvements');
lines.push(''); lines.push('');
lines.push( secondaryCommits.perf.forEach((perf) => {
'All changes in this release are included in Core and automatically available in Enterprise.' lines.push(perf);
); });
lines.push(''); lines.push('');
}
// No secondary changes message
if (!hasSecondaryChanges) {
lines.push('#### No additional changes');
lines.push('');
lines.push(
'All changes in this release are included in the primary repository.'
);
lines.push('');
}
} }
// Add common sections (breaking changes, API changes, etc.) // Add common sections (breaking changes, API changes, etc.)
@ -836,7 +900,7 @@ class ReleaseNotesGenerator {
commits.breaking.forEach((change) => { commits.breaking.forEach((change) => {
const pr = this.extractPrNumber(change); const pr = this.extractPrNumber(change);
const cleanLine = change.replace(/ \\(#\\d+\\)$/, ''); const cleanLine = change.replace(/ \\(#\\d+\\)$/, '');
if (pr) { if (pr && this.includePrLinks) {
lines.push( lines.push(
`${cleanLine} ([#${pr}](https://github.com/influxdata/influxdb/pull/${pr}))` `${cleanLine} ([#${pr}](https://github.com/influxdata/influxdb/pull/${pr}))`
); );
@ -854,7 +918,7 @@ class ReleaseNotesGenerator {
commits.api.forEach((api) => { commits.api.forEach((api) => {
const pr = this.extractPrNumber(api); const pr = this.extractPrNumber(api);
const cleanLine = api.replace(/ \\(#\\d+\\)$/, ''); const cleanLine = api.replace(/ \\(#\\d+\\)$/, '');
if (pr) { if (pr && this.includePrLinks) {
lines.push( lines.push(
`${cleanLine} ([#${pr}](https://github.com/influxdata/influxdb/pull/${pr}))` `${cleanLine} ([#${pr}](https://github.com/influxdata/influxdb/pull/${pr}))`
); );
@ -922,10 +986,10 @@ class ReleaseNotesGenerator {
// Generate output based on format // Generate output based on format
let content; let content;
if (this.config.outputFormat === 'core-enterprise') { if (this.config.outputFormat === 'separated') {
content = this.generateCoreEnterpriseFormat(commits, releaseDate); content = this.generateSeparatedFormat(commits, releaseDate);
} else { } else {
content = this.generateStandardFormat(commits, releaseDate); content = this.generateIntegratedFormat(commits, releaseDate);
} }
// Ensure output directory exists // Ensure output directory exists
@ -961,6 +1025,7 @@ function parseArgs() {
const options = { const options = {
fetchCommits: true, fetchCommits: true,
pullCommits: false, pullCommits: false,
includePrLinks: true,
config: { ...DEFAULT_CONFIG }, config: { ...DEFAULT_CONFIG },
}; };
@ -976,6 +1041,10 @@ function parseArgs() {
options.fetchCommits = true; options.fetchCommits = true;
i++; i++;
break; break;
case '--no-pr-links':
options.includePrLinks = false;
i++;
break;
case '--config': case '--config':
if (i + 1 >= args.length) { if (i + 1 >= args.length) {
console.error('Error: --config requires a configuration file path'); console.error('Error: --config requires a configuration file path');
@ -995,7 +1064,7 @@ function parseArgs() {
case '--format': case '--format':
if (i + 1 >= args.length) { if (i + 1 >= args.length) {
console.error( console.error(
'Error: --format requires a format type (standard|core-enterprise)' 'Error: --format requires a format type (integrated|separated)'
); );
process.exit(1); process.exit(1);
} }
@ -1039,22 +1108,16 @@ function parseArgs() {
options.fromVersion = options.fromVersion || 'v3.1.0'; options.fromVersion = options.fromVersion || 'v3.1.0';
options.toVersion = options.toVersion || 'v3.2.0'; options.toVersion = options.toVersion || 'v3.2.0';
// Detect Core/Enterprise format if influxdb and influxdb_pro are both present // Set default labels if not provided
if ( options.config.repositories.forEach((repo, index) => {
options.config.repositories.some((r) => r.name === 'influxdb') && if (!repo.label) {
options.config.repositories.some((r) => r.name === 'influxdb_pro') repo.label = repo.name || `repo${index + 1}`;
) { }
options.config.outputFormat = 'core-enterprise'; // Set default includePrLinks if not specified
if (repo.includePrLinks === undefined) {
// Set proper labels for Core/Enterprise repo.includePrLinks = options.includePrLinks;
options.config.repositories.forEach((repo) => { }
if (repo.name === 'influxdb') { });
repo.label = 'influxdb';
} else if (repo.name === 'influxdb_pro') {
repo.label = 'influxdb_pro';
}
});
}
return options; return options;
} }
@ -1066,8 +1129,9 @@ Usage: node generate-release-notes.js [options] <from_version> <to_version> <pri
Options: Options:
--no-fetch Skip fetching latest commits from remote --no-fetch Skip fetching latest commits from remote
--pull Pull latest changes (implies fetch) - use with caution --pull Pull latest changes (implies fetch) - use with caution
--no-pr-links Omit PR links from commit messages (default: include links)
--config <file> Load configuration from JSON file --config <file> Load configuration from JSON file
--format <type> Output format: 'standard' or 'core-enterprise' --format <type> Output format: 'integrated' or 'separated'
-h, --help Show this help message -h, --help Show this help message
Examples: Examples:
@ -1075,23 +1139,32 @@ Examples:
node generate-release-notes.js --no-fetch v3.1.0 v3.2.0 /path/to/influxdb node generate-release-notes.js --no-fetch v3.1.0 v3.2.0 /path/to/influxdb
node generate-release-notes.js --pull v3.1.0 v3.2.0 /path/to/influxdb /path/to/influxdb_pro node generate-release-notes.js --pull v3.1.0 v3.2.0 /path/to/influxdb /path/to/influxdb_pro
node generate-release-notes.js --config config.json v3.1.0 v3.2.0 node generate-release-notes.js --config config.json v3.1.0 v3.2.0
node generate-release-notes.js --format core-enterprise v3.1.0 v3.2.0 /path/to/influxdb /path/to/influxdb_pro node generate-release-notes.js --format separated v3.1.0 v3.2.0 /path/to/influxdb /path/to/influxdb_pro
Configuration file format (JSON): Configuration file format (JSON):
{ {
"outputFormat": "core-enterprise", "outputFormat": "separated",
"primaryRepo": "influxdb",
"repositories": [ "repositories": [
{ {
"name": "influxdb", "name": "influxdb",
"path": "/path/to/influxdb", "path": "/path/to/influxdb",
"label": "Core" "label": "Core",
"includePrLinks": true
}, },
{ {
"name": "influxdb_pro", "name": "influxdb_pro",
"path": "/path/to/influxdb_pro", "path": "/path/to/influxdb_pro",
"label": "Enterprise" "label": "Enterprise",
"includePrLinks": false
} }
] ],
"separatedTemplate": {
"header": "> [!Note]\\n> #### InfluxDB 3 Core and Enterprise relationship\\n>\\n> InfluxDB 3 Enterprise is a superset of InfluxDB 3 Core.\\n> All updates to Core are automatically included in Enterprise.\\n> The Enterprise sections below only list updates exclusive to Enterprise.",
"primaryLabel": "Core",
"secondaryLabel": "Enterprise",
"secondaryIntro": "All Core updates are included in Enterprise. Additional Enterprise-specific features and fixes:"
}
} }
`); `);
} }

View File

@ -2,14 +2,27 @@
A JavaScript ESM script to generate release notes for InfluxDB projects by analyzing git commits between two versions. A JavaScript ESM script to generate release notes for InfluxDB projects by analyzing git commits between two versions.
## InfluxDB 3 Core/Enterprise
This script supports the InfluxDB 3 Core/Enterprise relationship and tagged releases.
## InfluxDB 3 Clustered
See the Clustered [release process](https://github.com/influxdata/project-clustered?tab=readme-ov-file#release-process).
## Features ## Features
- **Flexible repository support**: Handle single or multiple repositories - **Flexible repository support**: Handle single or multiple repositories
- **Multiple output formats**: Standard format or Core/Enterprise format for InfluxDB 3.x - **Multiple output formats**:
- **Integrated**: All repositories' changes integrated in unified sections
- **Separated**: Primary repository first, then secondary repositories
- **Merge commit support**: Extracts features and fixes from merge commit bodies - **Merge commit support**: Extracts features and fixes from merge commit bodies
- **Conventional commit parsing**: Supports `feat:`, `fix:`, `perf:`, etc. - **Conventional commit parsing**: Supports `feat:`, `fix:`, `perf:`, etc.
- **PR link generation**: Automatically links to GitHub pull requests - **PR link generation**: Automatically links to GitHub pull requests (configurable per repository)
- **JSON configuration**: Configurable via command line or JSON config file - **JSON configuration**: Full configuration support via JSON files
- **Enhanced commit messages**: Categorizes commits based on affected areas (database, CLI, API, etc.)
- **Customizable templates**: Configure headers, labels, and intro text for separated format
## Usage ## Usage
@ -27,16 +40,24 @@ node generate-release-notes.js --no-fetch v3.1.0 v3.2.0 /path/to/repo
# Pull latest changes (use with caution) # Pull latest changes (use with caution)
node generate-release-notes.js --pull v3.1.0 v3.2.0 /path/to/repo node generate-release-notes.js --pull v3.1.0 v3.2.0 /path/to/repo
# Omit PR links from release notes
node generate-release-notes.js --no-pr-links v3.1.0 v3.2.0 /path/to/repo
``` ```
### Advanced Usage ### Advanced Usage
```bash ```bash
# Explicit format specification # Explicit format specification
node generate-release-notes.js --format core-enterprise v3.1.0 v3.2.0 /path/to/influxdb /path/to/influxdb_pro node generate-release-notes.js --format separated v3.1.0 v3.2.0 /path/to/influxdb /path/to/influxdb_pro
# Using JSON configuration # Using JSON configuration
node generate-release-notes.js --config config.json v3.1.0 v3.2.0 node generate-release-notes.js --config config.json v3.1.0 v3.2.0
# Using product-specific configurations
node generate-release-notes.js --config config/influxdb3-core-enterprise.json v3.2.0 v3.2.1
node generate-release-notes.js --config config/influxdb-v2.json v2.7.0 v2.7.1
node generate-release-notes.js --config config/influxdb3-clustered.json v1.0.0 v1.1.0
``` ```
### Configuration File ### Configuration File
@ -45,44 +66,68 @@ Create a JSON configuration file for complex setups:
```json ```json
{ {
"outputFormat": "core-enterprise", "outputFormat": "separated",
"primaryRepo": "influxdb",
"repositories": [ "repositories": [
{ {
"name": "influxdb", "name": "influxdb",
"path": "/path/to/influxdb", "path": "/path/to/influxdb",
"label": "influxdb" "label": "Core",
"includePrLinks": true
}, },
{ {
"name": "influxdb_pro", "name": "influxdb_pro",
"path": "/path/to/influxdb_pro", "path": "/path/to/influxdb_pro",
"label": "influxdb_pro" "label": "Enterprise",
"includePrLinks": false
} }
] ],
"separatedTemplate": {
"header": "> [!Note]\n> Custom header text here",
"primaryLabel": "Primary Repository",
"secondaryLabel": "Secondary Repositories",
"secondaryIntro": "Additional features and fixes from secondary repositories:"
}
} }
``` ```
## Output Formats ## Output Formats
### Standard Format ### Integrated Format
Basic release notes format with repository labels: All repositories' changes are integrated together in unified sections with repository labels and enhanced descriptions:
```markdown ```markdown
## v3.2.1 {date="2025-07-03"} ## v3.2.1 {date="2025-07-03"}
### Features ### Features
- [influxdb] feat: Allow hard_deleted date of deleted schema to be updated - [influxdb] **Database management**: Allow hard_deleted date of deleted schema to be updated ([#26574](https://github.com/influxdata/influxdb/pull/26574))
- [influxdb_pro] feat: amend license info (#987) - [influxdb_pro] **License management**: Amend license info ([#987](https://github.com/influxdata/influxdb/pull/987))
### Bug Fixes ### Bug Fixes
- [influxdb] fix: Add help text for the new update subcommand (#26569) - [influxdb] **CLI**: Add help text for the new update subcommand ([#26569](https://github.com/influxdata/influxdb/pull/26569))
``` ```
### Core/Enterprise Format When using `--no-pr-links`, the PR links are omitted:
InfluxDB 3.x specific format that separates Core and Enterprise changes: ```markdown
## v3.2.1 {date="2025-07-03"}
### Features
- [influxdb] **Database management**: Allow hard_deleted date of deleted schema to be updated
- [influxdb_pro] **License management**: Amend license info
### Bug Fixes
- [influxdb] **CLI**: Add help text for the new update subcommand
```
### Separated Format
Primary repository changes are shown first, followed by secondary repository changes. Ideal for products where one repository is a superset of another:
```markdown ```markdown
> [!Note] > [!Note]
@ -98,7 +143,7 @@ InfluxDB 3.x specific format that separates Core and Enterprise changes:
#### Features #### Features
- feat: Allow hard_deleted date of deleted schema to be updated - **Database management**: Allow hard_deleted date of deleted schema to be updated ([#26574](https://github.com/influxdata/influxdb/pull/26574))
### Enterprise ### Enterprise
@ -106,12 +151,27 @@ All Core updates are included in Enterprise. Additional Enterprise-specific feat
#### Features #### Features
- feat: amend license info (#987) - **License management**: Amend license info ([#987](https://github.com/influxdata/influxdb/pull/987))
``` ```
## Auto-Detection ## Configuration Options
The script automatically detects the Core/Enterprise format when both `influxdb` and `influxdb_pro` repositories are present. ### Repository Configuration
Each repository in the configuration can have:
- `name`: Repository identifier
- `path`: Path to the repository
- `label`: Label used in output
- `includePrLinks`: Whether to include PR links (boolean)
### Separated Format Template
When using separated format, you can customize:
- `header`: Markdown header text shown at the top
- `primaryLabel`: Section label for primary repository
- `secondaryLabel`: Section label for secondary repositories
- `secondaryIntro`: Introduction text for secondary section
- `primaryRepo`: Name or index of the primary repository
## Migration from Bash ## Migration from Bash
@ -137,6 +197,26 @@ Generated release notes are saved to `helper-scripts/output/release-notes/releas
- `--no-fetch`: Skip fetching latest commits from remote - `--no-fetch`: Skip fetching latest commits from remote
- `--pull`: Pull latest changes (implies fetch) - use with caution - `--pull`: Pull latest changes (implies fetch) - use with caution
- `--no-pr-links`: Omit PR links from commit messages (default: include links)
- `--config <file>`: Load configuration from JSON file - `--config <file>`: Load configuration from JSON file
- `--format <type>`: Output format: 'standard' or 'core-enterprise' - `--format <type>`: Output format: 'integrated' or 'separated'
- `-h, --help`: Show help message - `-h, --help`: Show help message
## Example Configurations
### InfluxDB 3 Core/Enterprise
See `config/influxdb3-core-enterprise.json` for a configuration that:
- Uses separated format
- Sets influxdb as primary repository (Core)
- Sets influxdb_pro as secondary repository (Enterprise)
- Includes PR links for Core, excludes them for Enterprise
- Adds custom header explaining the Core/Enterprise relationship
### InfluxDB v2
See `config/influxdb-v2.json` for a simple single-repository configuration using integrated format.
### InfluxDB 3 Clustered
See `config/influxdb3-clustered.json` for Kubernetes operator release notes.