UNPKG

@launchql/cli

Version:
187 lines (186 loc) • 7.82 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const core_1 = require("@launchql/core"); const core_2 = require("@launchql/core"); const logger_1 = require("@launchql/logger"); const fs_1 = require("fs"); const path_1 = require("path"); const pg_env_1 = require("pg-env"); const database_1 = require("../../utils/database"); const log = new logger_1.Logger('migrate-deps'); exports.default = async (argv, prompter, options) => { const cwd = argv.cwd || process.cwd(); const planPath = (0, path_1.join)(cwd, 'launchql.plan'); if (!(0, fs_1.existsSync)(planPath)) { log.error(`No launchql.plan found in ${cwd}`); process.exit(1); } // Get specific change to analyze let changeName = argv._?.[0] || argv.change; const planResult = (0, core_2.parsePlanFile)(planPath); if (!planResult.data || planResult.errors.length > 0) { log.error('Failed to parse plan file:', planResult.errors); process.exit(1); } const plan = planResult.data; const allChanges = plan.changes; // If no change specified, prompt if (!changeName && !argv.all) { const answer = await prompter.prompt(argv, [ { type: 'autocomplete', name: 'change', message: 'Which change do you want to analyze?', options: allChanges.map(c => ({ name: c.name, value: c.name, description: c.dependencies.length > 0 ? `Depends on: ${c.dependencies.join(', ')}` : 'No dependencies' })) } ]); changeName = answer.change; } try { if (argv.all) { // Show dependency graph for all changes console.log('\nšŸ”— Dependency Graph\n'); console.log(`Package: ${plan.package}`); console.log(`Total Changes: ${allChanges.length}\n`); // Build dependency tree const dependencyTree = buildDependencyTree(allChanges); // Show changes with no dependencies first (roots) const roots = allChanges.filter(c => c.dependencies.length === 0); console.log('šŸ“Œ Root Changes (no dependencies):\n'); roots.forEach(change => { console.log(` • ${change.name}`); showDependents(change.name, dependencyTree, ' '); }); // Show orphaned changes (have dependencies but aren't depended on) const orphans = allChanges.filter(c => c.dependencies.length > 0 && !allChanges.some(other => other.dependencies.includes(c.name))); if (orphans.length > 0) { console.log('\nšŸ”ø Leaf Changes (not depended on by others):\n'); orphans.forEach(change => { console.log(` • ${change.name} → [${change.dependencies.join(', ')}]`); }); } } else { // Show dependencies for specific change const change = allChanges.find(c => c.name === changeName); if (!change) { log.error(`Change '${changeName}' not found in plan file`); process.exit(1); } console.log(`\nšŸ” Dependency Analysis: ${changeName}\n`); // Direct dependencies if (change.dependencies.length > 0) { console.log('šŸ“„ Direct Dependencies:'); change.dependencies.forEach(dep => { console.log(` • ${dep}`); }); } else { console.log('šŸ“„ Direct Dependencies: None'); } // All dependencies (recursive) const allDeps = getAllDependencies(change.name, allChanges); if (allDeps.size > 0) { console.log(`\nšŸ“¦ All Dependencies (${allDeps.size} total):`); Array.from(allDeps).forEach(dep => { console.log(` • ${dep}`); }); } // Dependents (what depends on this) const dependents = allChanges.filter(c => c.dependencies.includes(changeName)); if (dependents.length > 0) { console.log(`\nšŸ“¤ Depended on by (${dependents.length} changes):`); dependents.forEach(dep => { console.log(` • ${dep.name}`); }); } else { console.log('\nšŸ“¤ Depended on by: None'); } // Check deployment status if connected to database const pgEnv = (0, pg_env_1.getPgEnvOptions)(); const targetDatabase = await (0, database_1.getTargetDatabase)(argv, prompter, { message: 'Select database to check deployment status' }); const client = new core_1.LaunchQLMigrate({ host: pgEnv.host, port: pgEnv.port, user: pgEnv.user, password: pgEnv.password, database: pgEnv.database }); try { const deployedChanges = await client.getDeployedChanges(targetDatabase, plan.package); const deployedMap = new Map(deployedChanges.map(c => [c.change_name, c])); console.log('\nšŸ“Š Deployment Status:'); // Check if this change is deployed const isDeployed = deployedMap.has(changeName); console.log(` This change: ${isDeployed ? 'āœ… Deployed' : 'ā³ Not deployed'}`); // Check dependencies const undeployedDeps = Array.from(allDeps).filter(dep => !deployedMap.has(dep)); if (undeployedDeps.length > 0) { console.log(` āš ļø Undeployed dependencies: ${undeployedDeps.join(', ')}`); } else if (allDeps.size > 0) { console.log(' āœ… All dependencies deployed'); } // Check dependents const deployedDependents = dependents.filter(d => deployedMap.has(d.name)); if (deployedDependents.length > 0) { console.log(` āš ļø Deployed dependents: ${deployedDependents.map(d => d.name).join(', ')}`); } } catch (dbError) { // Database connection optional for dependency analysis log.debug('Could not connect to database for deployment status'); } } } catch (error) { log.error('Failed to analyze dependencies:', error); process.exit(1); } }; function buildDependencyTree(changes) { const tree = new Map(); changes.forEach(change => { change.dependencies.forEach((dep) => { if (!tree.has(dep)) { tree.set(dep, []); } tree.get(dep).push(change.name); }); }); return tree; } function showDependents(changeName, tree, indent) { const dependents = tree.get(changeName) || []; dependents.forEach(dep => { console.log(`${indent}└─ ${dep}`); showDependents(dep, tree, indent + ' '); }); } function getAllDependencies(changeName, changes) { const deps = new Set(); const change = changes.find(c => c.name === changeName); if (!change) return deps; function addDeps(change) { change.dependencies.forEach((dep) => { if (!deps.has(dep)) { deps.add(dep); const depChange = changes.find(c => c.name === dep); if (depChange) { addDeps(depChange); } } }); } addDeps(change); return deps; }