#!/usr/bin/env node
/**
* Release Automation Script for LicenseSpring MCP Server
*
* This script provides utilities for managing automated releases:
* - Pre-release validation
* - Release preparation
* - Post-release verification
* - Release rollback capabilities
*/
import { execSync } from 'child_process';
import fs from 'fs';
import path from 'path';
const COLORS = {
reset: '\x1b[0m',
red: '\x1b[31m',
green: '\x1b[32m',
yellow: '\x1b[33m',
blue: '\x1b[34m',
magenta: '\x1b[35m',
cyan: '\x1b[36m'
};
function log(message, color = 'reset') {
console.log(`${COLORS[color]}${message}${COLORS.reset}`);
}
function exec(command, options = {}) {
try {
return execSync(command, { encoding: 'utf8', ...options });
} catch (error) {
log(`β Command failed: ${command}`, 'red');
log(`Error: ${error.message}`, 'red');
throw error;
}
}
class ReleaseAutomation {
constructor() {
this.packageJson = JSON.parse(fs.readFileSync('package.json', 'utf8'));
this.currentVersion = this.packageJson.version;
}
async validatePreRelease() {
log('π Running pre-release validation...', 'blue');
// Check if we're on the correct branch
const currentBranch = exec('git branch --show-current').trim();
if (!['main', 'master', 'develop'].includes(currentBranch)) {
throw new Error(`β Invalid branch for release: ${currentBranch}. Must be main, master, or develop.`);
}
log(`β
Branch check passed: ${currentBranch}`, 'green');
// Check for uncommitted changes
const gitStatus = exec('git status --porcelain').trim();
if (gitStatus) {
throw new Error('β Uncommitted changes detected. Please commit or stash changes before release.');
}
log('β
Git status clean', 'green');
// Run tests
log('π§ͺ Running test suite...', 'yellow');
exec('npm test');
log('β
Unit tests passed', 'green');
// Run linting
log('π Running linter...', 'yellow');
exec('npm run lint');
log('β
Linting passed', 'green');
// Build project
log('π¨ Building project...', 'yellow');
exec('npm run build');
log('β
Build successful', 'green');
// Check NPM authentication
try {
exec('npm whoami');
log('β
NPM authentication verified', 'green');
} catch (error) {
log('β οΈ NPM authentication not configured (will use CI token)', 'yellow');
}
log('π Pre-release validation completed successfully!', 'green');
}
async prepareRelease(releaseType = 'auto') {
log(`π Preparing ${releaseType} release...`, 'blue');
if (releaseType !== 'auto') {
// Manual version bump
const newVersion = exec(`npm version ${releaseType} --no-git-tag-version`).trim();
log(`π Version bumped to ${newVersion}`, 'green');
// Update package.json
exec('git add package.json package-lock.json');
exec(`git commit -m "chore(release): prepare ${newVersion}"`);
}
// Push to trigger semantic release
log('π€ Pushing to trigger automated release...', 'yellow');
exec('git push origin HEAD');
log('β
Release preparation completed!', 'green');
log('π Automated release workflow will now take over', 'cyan');
}
async verifyRelease(version) {
log(`π Verifying release ${version}...`, 'blue');
// Check NPM package
try {
const npmInfo = exec(`npm view @tfedorko/licensespring-mcp-server@${version} version`).trim();
if (npmInfo === version) {
log('β
NPM package published successfully', 'green');
} else {
throw new Error('NPM package version mismatch');
}
} catch (error) {
log('β NPM package verification failed', 'red');
throw error;
}
// Check GitHub release
try {
exec(`gh release view v${version}`);
log('β
GitHub release created successfully', 'green');
} catch (error) {
log('β οΈ GitHub CLI not available or release not found', 'yellow');
}
log('π Release verification completed!', 'green');
}
async rollbackRelease(version) {
log(`π Rolling back release ${version}...`, 'red');
try {
// Deprecate NPM package
exec(`npm deprecate @tfedorko/licensespring-mcp-server@${version} "Release rolled back due to issues"`);
log('β
NPM package deprecated', 'yellow');
// Delete GitHub release
try {
exec(`gh release delete v${version} --yes`);
log('β
GitHub release deleted', 'yellow');
} catch (error) {
log('β οΈ Could not delete GitHub release (may not exist)', 'yellow');
}
log('π Rollback completed successfully!', 'green');
} catch (error) {
log('β Rollback failed', 'red');
throw error;
}
}
async showReleaseStatus() {
log('π Release Status Dashboard', 'cyan');
log('=' * 50, 'cyan');
// Current version
log(`π¦ Current Version: ${this.currentVersion}`, 'blue');
// Latest NPM version
try {
const latestNpm = exec('npm view @tfedorko/licensespring-mcp-server version').trim();
log(`π Latest NPM: ${latestNpm}`, 'blue');
} catch (error) {
log('β Could not fetch NPM version', 'red');
}
// Recent releases
try {
const releases = exec('gh release list --limit 5').trim();
log('π Recent GitHub Releases:', 'blue');
console.log(releases);
} catch (error) {
log('β οΈ Could not fetch GitHub releases', 'yellow');
}
// Workflow status
try {
const workflows = exec('gh run list --limit 3').trim();
log('π Recent Workflow Runs:', 'blue');
console.log(workflows);
} catch (error) {
log('β οΈ Could not fetch workflow status', 'yellow');
}
}
}
// CLI Interface
async function main() {
const args = process.argv.slice(2);
const command = args[0];
const automation = new ReleaseAutomation();
try {
switch (command) {
case 'validate':
await automation.validatePreRelease();
break;
case 'prepare':
const releaseType = args[1] || 'auto';
await automation.prepareRelease(releaseType);
break;
case 'verify':
const version = args[1] || automation.currentVersion;
await automation.verifyRelease(version);
break;
case 'rollback':
const rollbackVersion = args[1];
if (!rollbackVersion) {
throw new Error('β Version required for rollback');
}
await automation.rollbackRelease(rollbackVersion);
break;
case 'status':
await automation.showReleaseStatus();
break;
default:
log('π LicenseSpring MCP Server Release Automation', 'cyan');
log('', 'reset');
log('Usage:', 'yellow');
log(' npm run release:validate - Run pre-release validation', 'reset');
log(' npm run release:prepare - Prepare automated release', 'reset');
log(' npm run release:verify <ver> - Verify release deployment', 'reset');
log(' npm run release:rollback <v> - Rollback a release', 'reset');
log(' npm run release:status - Show release status', 'reset');
log('', 'reset');
log('Examples:', 'yellow');
log(' npm run release:prepare patch', 'reset');
log(' npm run release:verify 1.2.3', 'reset');
log(' npm run release:rollback 1.2.3', 'reset');
}
} catch (error) {
log(`β ${error.message}`, 'red');
process.exit(1);
}
}
if (import.meta.url === `file://${process.argv[1]}`) {
main();
}
export default ReleaseAutomation;