@launchql/cli
Version:
LaunchQL CLI
187 lines (186 loc) ⢠7.82 kB
JavaScript
;
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;
}