@re-shell/cli
Version:
Full-stack development platform uniting microservices and microfrontends. Build complete applications with .NET (ASP.NET Core Web API, Minimal API), Java (Spring Boot, Quarkus, Micronaut, Vert.x), Rust (Actix-Web, Warp, Rocket, Axum), Python (FastAPI, Dja
458 lines (457 loc) • 18.4 kB
JavaScript
;
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.migrationManager = exports.MigrationManager = void 0;
exports.autoMigrate = autoMigrate;
exports.migrateGlobalConfig = migrateGlobalConfig;
exports.migrateProjectConfig = migrateProjectConfig;
exports.checkConfigIntegrity = checkConfigIntegrity;
const fs = __importStar(require("fs-extra"));
const path = __importStar(require("path"));
const yaml = __importStar(require("yaml"));
const config_1 = require("./config");
const error_handler_1 = require("./error-handler");
const semver_1 = __importDefault(require("semver"));
// Configuration version history and migrations
const CURRENT_CONFIG_VERSION = '1.2.0';
const GLOBAL_CONFIG_MIGRATIONS = [
{
version: '1.0.1',
description: 'Add CLI theme support',
up: (config) => ({
...config,
cli: {
...config.cli,
theme: config.cli?.theme || 'auto'
}
}),
validate: (config) => config.cli && typeof config.cli.theme === 'string'
},
{
version: '1.1.0',
description: 'Add plugin marketplace configuration',
up: (config) => ({
...config,
plugins: {
...config.plugins,
marketplace: {
registry: 'https://registry.npmjs.org',
autoUpdate: false,
...config.plugins?.marketplace
}
}
}),
validate: (config) => config.plugins?.marketplace?.registry
},
{
version: '1.2.0',
description: 'Add user profile and enhanced paths',
up: (config) => ({
...config,
user: {
name: undefined,
email: undefined,
organization: undefined,
...config.user
},
paths: {
...config.paths,
workspace: path.join(config.paths?.cache || '~/.re-shell/cache', 'workspaces'),
logs: path.join(config.paths?.cache || '~/.re-shell/cache', 'logs')
}
}),
validate: (config) => config.user && config.paths?.workspace && config.paths?.logs
}
];
const PROJECT_CONFIG_MIGRATIONS = [
{
version: '1.0.1',
description: 'Add environment configurations',
up: (config) => ({
...config,
environments: config.environments || {
development: {
name: 'development',
variables: { NODE_ENV: 'development' },
build: { mode: 'development', optimization: false, sourcemaps: true },
deployment: {}
},
staging: {
name: 'staging',
variables: { NODE_ENV: 'staging' },
build: { mode: 'staging', optimization: true, sourcemaps: true },
deployment: {}
},
production: {
name: 'production',
variables: { NODE_ENV: 'production' },
build: { mode: 'production', optimization: true, sourcemaps: false },
deployment: {}
}
}
}),
validate: (config) => config.environments && Object.keys(config.environments).length > 0
},
{
version: '1.1.0',
description: 'Enhanced workspace configuration',
up: (config) => ({
...config,
workspaces: {
root: '.',
patterns: ['apps/*', 'packages/*', 'libs/*', 'tools/*'],
types: ['app', 'package', 'lib', 'tool'],
...config.workspaces
}
}),
validate: (config) => config.workspaces?.patterns && Array.isArray(config.workspaces.patterns)
},
{
version: '1.2.0',
description: 'Add quality and security configurations',
up: (config) => ({
...config,
quality: {
linting: true,
testing: true,
coverage: { enabled: true, threshold: 80 },
security: { enabled: true, autoFix: false },
...config.quality
}
}),
validate: (config) => config.quality && typeof config.quality.linting === 'boolean'
}
];
// Migration manager class
class MigrationManager {
constructor() {
this.migrations = new Map();
this.migrations.set('global', GLOBAL_CONFIG_MIGRATIONS);
this.migrations.set('project', PROJECT_CONFIG_MIGRATIONS);
}
// Check if migration is needed
async needsMigration(configType, currentVersion) {
const version = currentVersion || await this.getCurrentVersion(configType);
return semver_1.default.lt(version, CURRENT_CONFIG_VERSION);
}
// Get current configuration version
async getCurrentVersion(configType, projectPath) {
try {
if (configType === 'global') {
const globalConfig = await config_1.configManager.loadGlobalConfig();
return globalConfig.version || '1.0.0';
}
else {
const projectConfig = await config_1.configManager.loadProjectConfig(projectPath);
return projectConfig?.version || '1.0.0';
}
}
catch {
return '1.0.0';
}
}
// Get available migrations for version range
getAvailableMigrations(configType, fromVersion, toVersion = CURRENT_CONFIG_VERSION) {
const migrations = this.migrations.get(configType) || [];
return migrations.filter(migration => {
return semver_1.default.gt(migration.version, fromVersion) && semver_1.default.lte(migration.version, toVersion);
}).sort((a, b) => semver_1.default.compare(a.version, b.version));
}
// Apply migrations
async migrate(configType, projectPath) {
const fromVersion = await this.getCurrentVersion(configType, projectPath);
const toVersion = CURRENT_CONFIG_VERSION;
if (!await this.needsMigration(configType, fromVersion)) {
return {
success: true,
fromVersion,
toVersion,
appliedMigrations: [],
warnings: ['Configuration is already up to date']
};
}
const migrations = this.getAvailableMigrations(configType, fromVersion, toVersion);
const appliedMigrations = [];
const errors = [];
const warnings = [];
try {
// Load current configuration
let config;
if (configType === 'global') {
config = await config_1.configManager.loadGlobalConfig();
}
else {
config = await config_1.configManager.loadProjectConfig(projectPath);
if (!config) {
throw new error_handler_1.ValidationError('No project configuration found');
}
}
// Create backup before migration
const backupPath = await this.createMigrationBackup(configType, config, projectPath);
warnings.push(`Backup created at: ${backupPath}`);
// Apply migrations sequentially
for (const migration of migrations) {
try {
console.log(`Applying migration ${migration.version}: ${migration.description}`);
// Apply the migration
config = migration.up(config);
config.version = migration.version;
// Validate the result if validation function exists
if (migration.validate && !migration.validate(config)) {
throw new Error(`Migration validation failed for version ${migration.version}`);
}
appliedMigrations.push(migration.version);
}
catch (error) {
errors.push(`Migration ${migration.version} failed: ${error.message}`);
break;
}
}
// Save migrated configuration if no errors
if (errors.length === 0) {
config.version = toVersion;
if (configType === 'global') {
await config_1.configManager.saveGlobalConfig(config);
}
else {
await config_1.configManager.saveProjectConfig(config, projectPath);
}
}
return {
success: errors.length === 0,
fromVersion,
toVersion,
appliedMigrations,
errors: errors.length > 0 ? errors : undefined,
warnings
};
}
catch (error) {
return {
success: false,
fromVersion,
toVersion,
appliedMigrations,
errors: [`Migration failed: ${error.message}`]
};
}
}
// Rollback to previous version
async rollback(configType, targetVersion, projectPath) {
const currentVersion = await this.getCurrentVersion(configType, projectPath);
if (semver_1.default.gte(targetVersion, currentVersion)) {
return {
success: false,
fromVersion: currentVersion,
toVersion: targetVersion,
appliedMigrations: [],
errors: ['Target version must be lower than current version']
};
}
// Get migrations to rollback (in reverse order)
const migrations = this.getAvailableMigrations(configType, targetVersion, currentVersion);
const reversedMigrations = migrations.reverse();
const appliedMigrations = [];
const errors = [];
try {
// Load current configuration
let config;
if (configType === 'global') {
config = await config_1.configManager.loadGlobalConfig();
}
else {
config = await config_1.configManager.loadProjectConfig(projectPath);
}
// Apply rollback migrations
for (const migration of reversedMigrations) {
if (migration.down) {
try {
config = migration.down(config);
appliedMigrations.push(`rollback-${migration.version}`);
}
catch (error) {
errors.push(`Rollback ${migration.version} failed: ${error.message}`);
break;
}
}
else {
errors.push(`No rollback available for migration ${migration.version}`);
break;
}
}
// Save rolled back configuration
if (errors.length === 0) {
config.version = targetVersion;
if (configType === 'global') {
await config_1.configManager.saveGlobalConfig(config);
}
else {
await config_1.configManager.saveProjectConfig(config, projectPath);
}
}
return {
success: errors.length === 0,
fromVersion: currentVersion,
toVersion: targetVersion,
appliedMigrations,
errors: errors.length > 0 ? errors : undefined
};
}
catch (error) {
return {
success: false,
fromVersion: currentVersion,
toVersion: targetVersion,
appliedMigrations,
errors: [`Rollback failed: ${error.message}`]
};
}
}
// Create migration backup
async createMigrationBackup(configType, config, projectPath) {
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
const backupDir = configType === 'global'
? path.join(process.env.HOME || '~', '.re-shell', 'backups', 'migrations')
: path.join(projectPath || process.cwd(), '.re-shell', 'backups', 'migrations');
const backupPath = path.join(backupDir, `${configType}-config-${timestamp}.yaml`);
await fs.ensureDir(backupDir);
const content = yaml.stringify(config);
await fs.writeFile(backupPath, content, 'utf8');
return backupPath;
}
// Check configuration integrity
async checkIntegrity(configType, projectPath) {
const issues = [];
const recommendations = [];
try {
const version = await this.getCurrentVersion(configType, projectPath);
// Check if migration is needed
if (await this.needsMigration(configType, version)) {
issues.push(`Configuration version ${version} is outdated (current: ${CURRENT_CONFIG_VERSION})`);
recommendations.push('Run migration to update to latest version');
}
// Load and validate configuration
let config;
if (configType === 'global') {
config = await config_1.configManager.loadGlobalConfig();
}
else {
config = await config_1.configManager.loadProjectConfig(projectPath);
if (!config) {
issues.push('No project configuration found');
recommendations.push('Initialize project configuration');
return { valid: false, version, issues, recommendations };
}
}
// Validate against current schema
const migrations = this.getAvailableMigrations(configType, '1.0.0', CURRENT_CONFIG_VERSION);
for (const migration of migrations) {
if (migration.validate && !migration.validate(config)) {
issues.push(`Configuration does not satisfy requirements for version ${migration.version}`);
}
}
return {
valid: issues.length === 0,
version,
issues,
recommendations
};
}
catch (error) {
return {
valid: false,
version: '1.0.0',
issues: [`Configuration check failed: ${error.message}`],
recommendations: ['Check configuration file syntax and permissions']
};
}
}
// Auto-migrate on CLI startup if needed
async autoMigrate() {
const results = { global: null, project: null };
// Auto-migrate global configuration
if (await this.needsMigration('global')) {
results.global = await this.migrate('global');
}
// Auto-migrate project configuration if in a project directory
try {
const projectConfig = await config_1.configManager.loadProjectConfig();
if (projectConfig && await this.needsMigration('project')) {
results.project = await this.migrate('project');
}
}
catch {
// Not in a project directory, skip project migration
}
return results;
}
// Get migration history
async getMigrationHistory(configType, projectPath) {
const currentVersion = await this.getCurrentVersion(configType, projectPath);
const allMigrations = this.migrations.get(configType) || [];
const availableVersions = allMigrations.map(m => m.version);
const appliedMigrations = allMigrations
.filter(m => semver_1.default.lte(m.version, currentVersion))
.map(m => m.version);
const pendingMigrations = allMigrations
.filter(m => semver_1.default.gt(m.version, currentVersion))
.map(m => m.version);
return {
currentVersion,
availableVersions,
appliedMigrations,
pendingMigrations
};
}
}
exports.MigrationManager = MigrationManager;
// Export singleton instance
exports.migrationManager = new MigrationManager();
// Helper functions
async function autoMigrate() {
return exports.migrationManager.autoMigrate();
}
async function migrateGlobalConfig() {
return exports.migrationManager.migrate('global');
}
async function migrateProjectConfig(projectPath) {
return exports.migrationManager.migrate('project', projectPath);
}
async function checkConfigIntegrity(configType, projectPath) {
return exports.migrationManager.checkIntegrity(configType, projectPath);
}