UNPKG

aios-core

Version:

Synkra AIOS: AI-Orchestrated System for Full Stack Development - Core Framework

293 lines (250 loc) 7.83 kB
/** * Migration Execution Module * * Executes the migration from v2.0 → v4.0.4 structure. * * @module cli/commands/migrate/execute * @version 1.0.0 * @story 2.14 - Migration Script v2.0 → v4.0.4 */ const fs = require('fs'); const path = require('path'); const { copyFileWithMetadata } = require('./backup'); const { MODULE_MAPPING } = require('./analyze'); /** * Create module directories for v4.0.4 structure * @param {string} aiosCoreDir - Path to .aios-core * @param {Object} options - Options * @returns {Promise<Object>} Created directories */ async function createModuleDirectories(aiosCoreDir, options = {}) { const { onProgress = () => {} } = options; const modules = Object.keys(MODULE_MAPPING); const created = []; onProgress({ phase: 'directories', message: 'Creating module directories...' }); for (const moduleName of modules) { const moduleDir = path.join(aiosCoreDir, moduleName); if (!fs.existsSync(moduleDir)) { await fs.promises.mkdir(moduleDir, { recursive: true }); created.push(moduleDir); onProgress({ phase: 'directory', message: ` ✓ Created ${moduleName}/` }); } } return { created, modules }; } /** * Migrate files for a single module * @param {Object} moduleData - Module data from migration plan * @param {string} moduleName - Module name * @param {string} aiosCoreDir - Path to .aios-core * @param {Object} options - Options * @returns {Promise<Object>} Migration result */ async function migrateModule(moduleData, moduleName, aiosCoreDir, options = {}) { const { verbose = false, onProgress = () => {}, dryRun = false } = options; const result = { module: moduleName, migratedFiles: [], errors: [], totalSize: 0, }; const moduleDir = path.join(aiosCoreDir, moduleName); for (const file of moduleData.files) { try { // Calculate target path within the module const targetPath = path.join(moduleDir, file.relativePath); if (dryRun) { result.migratedFiles.push({ source: file.sourcePath, target: targetPath, size: file.size, dryRun: true, }); } else { // Actually copy the file await copyFileWithMetadata(file.sourcePath, targetPath); result.migratedFiles.push({ source: file.sourcePath, target: targetPath, size: file.size, }); } result.totalSize += file.size; if (verbose) { onProgress({ phase: 'file', message: ` → ${file.relativePath}`, }); } } catch (error) { result.errors.push({ file: file.relativePath, error: error.message, }); } } return result; } /** * Execute full migration from v2.0 to v4.0.4 * @param {Object} plan - Migration plan from analyzeMigrationPlan * @param {Object} options - Execution options * @returns {Promise<Object>} Migration result */ async function executeMigration(plan, options = {}) { const { verbose = false, dryRun = false, onProgress = () => {}, cleanupOriginals = true, } = options; if (!plan.canMigrate) { return { success: false, error: plan.error || plan.message, }; } const result = { success: true, dryRun, modules: {}, totalFiles: 0, totalSize: 0, errors: [], cleanedUp: [], }; // Phase 1: Create module directories if (!dryRun) { await createModuleDirectories(plan.aiosCoreDir, { onProgress }); } // Phase 2: Migrate each module const moduleNames = ['core', 'development', 'product', 'infrastructure']; for (const moduleName of moduleNames) { const moduleData = plan.modules[moduleName]; if (moduleData.files.length === 0) { result.modules[moduleName] = { files: 0, size: 0, skipped: true, }; continue; } onProgress({ phase: 'module', message: `✓ Migrating ${moduleName.charAt(0).toUpperCase() + moduleName.slice(1)} module (${moduleData.files.length} files)`, }); const moduleResult = await migrateModule( moduleData, moduleName, plan.aiosCoreDir, { verbose, onProgress, dryRun }, ); result.modules[moduleName] = { files: moduleResult.migratedFiles.length, size: moduleResult.totalSize, errors: moduleResult.errors, }; result.totalFiles += moduleResult.migratedFiles.length; result.totalSize += moduleResult.totalSize; if (moduleResult.errors.length > 0) { result.errors.push(...moduleResult.errors); } } // Handle uncategorized files (move to core/) if (plan.uncategorized.length > 0 && !dryRun) { onProgress({ phase: 'uncategorized', message: `Handling ${plan.uncategorized.length} uncategorized files...`, }); for (const file of plan.uncategorized) { try { const targetPath = path.join(plan.aiosCoreDir, 'core', file.relativePath); await copyFileWithMetadata(file.sourcePath, targetPath); result.totalFiles++; result.totalSize += file.size; } catch (error) { result.errors.push({ file: file.relativePath, error: error.message, }); } } } // Phase 3: Cleanup original files (if not dry run and enabled) if (!dryRun && cleanupOriginals) { onProgress({ phase: 'cleanup', message: 'Cleaning up original locations...' }); // Get directories that should be removed (now nested in modules) const dirsToCleanup = new Set(); for (const [_moduleName, config] of Object.entries(MODULE_MAPPING)) { for (const dir of config.directories) { const originalDir = path.join(plan.aiosCoreDir, dir); if (fs.existsSync(originalDir)) { dirsToCleanup.add(originalDir); } } } for (const dir of dirsToCleanup) { try { // Check this is not a module directory itself const dirName = path.basename(dir); if (!['core', 'development', 'product', 'infrastructure'].includes(dirName)) { await fs.promises.rm(dir, { recursive: true }); result.cleanedUp.push(dir); } } catch (error) { // Non-fatal: cleanup errors are logged but don't fail migration result.errors.push({ type: 'cleanup', path: dir, error: error.message, }); } } } // Final status result.success = result.errors.filter(e => e.type !== 'cleanup').length === 0; return result; } /** * Create a migration state file to track progress * @param {string} projectRoot - Project root * @param {Object} state - Migration state */ async function saveMigrationState(projectRoot, state) { const statePath = path.join(projectRoot, '.aios-migration-state.json'); await fs.promises.writeFile(statePath, JSON.stringify({ ...state, timestamp: new Date().toISOString(), }, null, 2)); } /** * Load migration state if exists * @param {string} projectRoot - Project root * @returns {Object|null} Migration state or null */ async function loadMigrationState(projectRoot) { const statePath = path.join(projectRoot, '.aios-migration-state.json'); if (fs.existsSync(statePath)) { const content = await fs.promises.readFile(statePath, 'utf8'); return JSON.parse(content); } return null; } /** * Clear migration state file * @param {string} projectRoot - Project root */ async function clearMigrationState(projectRoot) { const statePath = path.join(projectRoot, '.aios-migration-state.json'); if (fs.existsSync(statePath)) { await fs.promises.unlink(statePath); } } module.exports = { createModuleDirectories, migrateModule, executeMigration, saveMigrationState, loadMigrationState, clearMigrationState, };