Atlassian Confluence MCP Server
by aashari
Verified
#!/usr/bin/env node
/**
* Script to update version numbers across the project
* Usage: node scripts/update-version.js [version] [options]
* Options:
* --dry-run Show what changes would be made without applying them
* --verbose Show detailed logging information
*
* If no version is provided, it will use the version from package.json
*/
import fs from 'fs';
import path from 'path';
import { fileURLToPath } from 'url';
// Get the directory name of the current module
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const rootDir = path.resolve(__dirname, '..');
// Parse command line arguments
const args = process.argv.slice(2);
const options = {
dryRun: args.includes('--dry-run'),
verbose: args.includes('--verbose'),
};
// Get the version (first non-flag argument)
let newVersion = args.find((arg) => !arg.startsWith('--'));
// Log helper function
const log = (message, verbose = false) => {
if (!verbose || options.verbose) {
console.log(message);
}
};
// File paths that may contain version information
const versionFiles = [
{
path: path.join(rootDir, 'package.json'),
pattern: /"version": "([^"]*)"/,
replacement: (match, currentVersion) =>
match.replace(currentVersion, newVersion),
},
{
path: path.join(rootDir, 'src', 'cli', 'index.ts'),
pattern: /const VERSION = ['"]([^'"]*)['"]/,
replacement: (match, currentVersion) =>
match.replace(currentVersion, newVersion),
},
{
path: path.join(rootDir, 'src', 'index.ts'),
pattern: /const VERSION = ['"]([^'"]*)['"]/,
replacement: (match, currentVersion) =>
match.replace(currentVersion, newVersion),
},
// Also update the compiled JavaScript files if they exist
{
path: path.join(rootDir, 'dist', 'cli', 'index.js'),
pattern: /const VERSION = ['"]([^'"]*)['"]/,
replacement: (match, currentVersion) =>
match.replace(currentVersion, newVersion),
optional: true, // Mark this file as optional
},
{
path: path.join(rootDir, 'dist', 'index.js'),
pattern: /const VERSION = ['"]([^'"]*)['"]/,
replacement: (match, currentVersion) =>
match.replace(currentVersion, newVersion),
optional: true, // Mark this file as optional
},
// Additional files can be added here with their patterns and replacement logic
];
/**
* Read the version from package.json
* @returns {string} The version from package.json
*/
function getPackageVersion() {
try {
const packageJsonPath = path.join(rootDir, 'package.json');
log(`Reading version from ${packageJsonPath}`, true);
const packageJson = JSON.parse(
fs.readFileSync(packageJsonPath, 'utf8'),
);
if (!packageJson.version) {
throw new Error('No version field found in package.json');
}
return packageJson.version;
} catch (error) {
console.error(`Error reading package.json: ${error.message}`);
process.exit(1);
}
}
/**
* Validate the semantic version format
* @param {string} version - The version to validate
* @returns {boolean} True if valid, throws error if invalid
*/
function validateVersion(version) {
// More comprehensive semver regex
const semverRegex =
/^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$/;
if (!semverRegex.test(version)) {
throw new Error(
`Invalid version format: ${version}\nPlease use semantic versioning format (e.g., 1.2.3, 1.2.3-beta.1, etc.)`,
);
}
return true;
}
/**
* Update version in a specific file
* @param {Object} fileConfig - Configuration for the file to update
*/
function updateFileVersion(fileConfig) {
const {
path: filePath,
pattern,
replacement,
optional = false,
} = fileConfig;
try {
log(`Checking ${filePath}...`, true);
if (!fs.existsSync(filePath)) {
if (optional) {
log(`Optional file not found (skipping): ${filePath}`, true);
return;
}
console.warn(`Warning: File not found: ${filePath}`);
return;
}
// Read file content
const fileContent = fs.readFileSync(filePath, 'utf8');
const match = fileContent.match(pattern);
if (!match) {
console.warn(`Warning: Version pattern not found in ${filePath}`);
return;
}
const currentVersion = match[1];
if (currentVersion === newVersion) {
log(
`Version in ${path.basename(filePath)} is already ${newVersion}`,
true,
);
return;
}
// Create new content with the updated version
const updatedContent = fileContent.replace(pattern, replacement);
// Write the changes or log them in dry run mode
if (options.dryRun) {
log(
`Would update version in ${filePath} from ${currentVersion} to ${newVersion}`,
);
} else {
// Create a backup of the original file
fs.writeFileSync(`${filePath}.bak`, fileContent);
log(`Backup created: ${filePath}.bak`, true);
// Write the updated content
fs.writeFileSync(filePath, updatedContent);
log(
`Updated version in ${path.basename(filePath)} from ${currentVersion} to ${newVersion}`,
);
}
} catch (error) {
if (optional) {
log(`Error with optional file ${filePath}: ${error.message}`, true);
return;
}
console.error(`Error updating ${filePath}: ${error.message}`);
process.exit(1);
}
}
// Main execution
try {
// If no version specified, get from package.json
if (!newVersion) {
newVersion = getPackageVersion();
log(
`No version specified, using version from package.json: ${newVersion}`,
);
}
// Validate the version format
validateVersion(newVersion);
// Update all configured files
for (const fileConfig of versionFiles) {
updateFileVersion(fileConfig);
}
if (options.dryRun) {
log(`\nDry run completed. No files were modified.`);
} else {
log(`\nVersion successfully updated to ${newVersion}`);
}
} catch (error) {
console.error(`\nVersion update failed: ${error.message}`);
process.exit(1);
}