@launchql/core
Version:
LaunchQL Package and Migration Tools
124 lines (123 loc) • 6.41 kB
JavaScript
import { getEnvOptions } from '@launchql/env';
import { Logger } from '@launchql/logger';
import { errors } from '@launchql/types';
import { resolve } from 'path';
import * as path from 'path';
import { getPgPool } from 'pg-cache';
import { LaunchQLPackage } from '../core/class/launchql';
import { LaunchQLMigrate } from '../migrate/client';
import { packageModule } from '../packaging/package';
// Cache for fast deployment
const deployFastCache = {};
const getCacheKey = (pg, name, database) => {
const { host, port, user } = pg ?? {};
return `${host}:${port}:${user}:${database}:${name}`;
};
const log = new Logger('deploy');
export const deployProject = async (opts, name, database, pkg, toChange) => {
const mergedOpts = getEnvOptions(opts);
log.info(`🔍 Gathering modules from ${pkg.workspacePath}...`);
const modules = pkg.getModuleMap();
if (!modules[name]) {
log.error(`❌ Module "${name}" not found in modules list.`);
throw errors.MODULE_NOT_FOUND({ name });
}
const modulePath = path.resolve(pkg.workspacePath, modules[name].path);
const moduleProject = new LaunchQLPackage(modulePath);
log.info(`📦 Resolving dependencies for ${name}...`);
const extensions = moduleProject.getModuleExtensions();
const pgPool = getPgPool({ ...opts.pg, database });
log.success(`🚀 Starting deployment to database ${database}...`);
for (const extension of extensions.resolved) {
try {
if (extensions.external.includes(extension)) {
const msg = `CREATE EXTENSION IF NOT EXISTS "${extension}" CASCADE;`;
log.info(`📥 Installing external extension: ${extension}`);
log.debug(`> ${msg}`);
await pgPool.query(msg);
}
else {
const modulePath = resolve(pkg.workspacePath, modules[extension].path);
log.info(`📂 Deploying local module: ${extension}`);
log.debug(`→ Path: ${modulePath}`);
if (mergedOpts.deployment.fast) {
// Use fast deployment strategy
const localProject = new LaunchQLPackage(modulePath);
const cacheKey = getCacheKey(mergedOpts.pg, extension, database);
if (mergedOpts.deployment.cache && deployFastCache[cacheKey]) {
log.warn(`⚡ Using cached package for ${extension}.`);
await pgPool.query(deployFastCache[cacheKey].sql);
continue;
}
let modulePackage;
try {
modulePackage = await packageModule(localProject.modulePath, {
usePlan: mergedOpts.deployment.usePlan,
extension: false
});
}
catch (err) {
// Build comprehensive error message
const errorLines = [];
errorLines.push(`❌ Failed to package module "${extension}" at path: ${modulePath}`);
errorLines.push(` Module Path: ${modulePath}`);
errorLines.push(` Workspace Path: ${pkg.workspacePath}`);
errorLines.push(` Error Code: ${err.code || 'N/A'}`);
errorLines.push(` Error Message: ${err.message || 'Unknown error'}`);
// Provide debugging hints
if (err.code === 'ENOENT') {
errorLines.push('💡 Hint: File or directory not found. Check if the module path is correct.');
}
else if (err.code === 'EACCES') {
errorLines.push('💡 Hint: Permission denied. Check file permissions.');
}
else if (err.message && err.message.includes('launchql.plan')) {
errorLines.push('💡 Hint: launchql.plan file issue. Check if the plan file exists and is valid.');
}
// Log the consolidated error message
log.error(errorLines.join('\n'));
console.error(err); // Preserve full stack trace
throw errors.DEPLOYMENT_FAILED({
type: 'Deployment',
module: extension
});
}
log.debug(`→ Command: sqitch deploy db:pg:${database}`);
log.debug(`> ${modulePackage.sql}`);
await pgPool.query(modulePackage.sql);
if (mergedOpts.deployment.cache) {
deployFastCache[cacheKey] = modulePackage;
}
}
else {
// Use new migration system
log.debug(`→ Command: launchql migrate deploy db:pg:${database}`);
try {
const client = new LaunchQLMigrate(mergedOpts.pg);
const result = await client.deploy({
modulePath,
toChange,
useTransaction: mergedOpts.deployment.useTx,
logOnly: mergedOpts.deployment.logOnly
});
if (result.failed) {
throw errors.OPERATION_FAILED({ operation: 'Deployment', target: result.failed });
}
}
catch (deployError) {
log.error(`❌ Deployment failed for module ${extension}`);
console.error(deployError);
throw errors.DEPLOYMENT_FAILED({ type: 'Deployment', module: extension });
}
}
}
}
catch (err) {
log.error(`🛑 Error during deployment: ${err instanceof Error ? err.message : err}`);
console.error(err); // Keep raw error output for stack traces
throw errors.DEPLOYMENT_FAILED({ type: 'Deployment', module: extension });
}
}
log.success(`✅ Deployment complete for ${name}.`);
return extensions;
};